feat: Setup up dev enviroment

This commit is contained in:
Pete
2024-11-29 14:42:02 +01:00
committed by MariaPaula Trujillo
parent 9a85cf8108
commit 43df35811d
11 changed files with 958 additions and 125 deletions

4
.env
View File

@ -1 +1,3 @@
SECRET_KEY=insecure_dev_key
SECRET_KEY=insecure_dev_key
PORT=8052
FLASK_DEBUG=true

3
.gitignore vendored
View File

@ -21,9 +21,12 @@ package-lock.json
# [env] Local environment settings
.*.hash
.env.local
# [sass] Files generated by Sass
*.css
# Project specific
.venv/
.dotrun.json

View File

@ -1 +0,0 @@

28
.stylelintrc Normal file
View File

@ -0,0 +1,28 @@
{
"extends": ["stylelint-config-standard-scss"],
"rules": {
"no-invalid-position-at-import-rule": null,
"scss/at-mixin-pattern": null,
"declaration-empty-line-before": null,
"scss/operator-no-newline-after": null,
"scss/dollar-variable-pattern": null,
"scss/double-slash-comment-empty-line-before": null,
"scss/no-global-function-names": null,
"selector-class-pattern": null,
"scss/at-extend-no-missing-placeholder": null,
"at-rule-no-unknown": [ true, {
"ignoreAtRules": [
"extend",
"include",
"mixin",
"for",
"function",
"if",
"else",
"warn",
"return",
"use"
]
} ]
}
}

View File

@ -7,12 +7,6 @@ This is the codebase for the canonical.design static website
This site is currently under-development.
## Development
The simplest way to run the site is:
The simplest way to run the site is with `dotrun`.
- Create a python virtual environment
- install requirements
- install node modules
- run `yarn build`
- run `flask run` in webapp folder
Afterwards the website will be available at <http://localhost:5000>.
Afterwards the website will be available at <http://localhost:8052>.

11
entrypoint Executable file
View File

@ -0,0 +1,11 @@
#! /usr/bin/env bash
set -e
RUN_COMMAND="talisker.gunicorn webapp.app:app --bind $1 --name talisker-`hostname`"
if [ "${FLASK_DEBUG}" = true ] || [ "${FLASK_DEBUG}" = 1 ]; then
RUN_COMMAND="${RUN_COMMAND} --reload --log-level debug --timeout 9999"
fi
${RUN_COMMAND}

View File

@ -2,8 +2,19 @@
"name": "canonical-design",
"description": "marketing site for design team at Canonical",
"scripts": {
"start": "yarn run build && concurrently --raw 'yarn run watch' 'yarn run serve'",
"serve": "./entrypoint 0.0.0.0:${PORT}",
"watch": "yarn run watch-css",
"watch-css": "watch -p 'static/sass/**/*.scss' -c 'yarn run build'",
"build": "yarn run build-css",
"build-css": "sass static/sass/styles.scss static/css/styles.css --load-path=node_modules --style=compressed && postcss --map false --use autoprefixer --replace 'static/css/**/*.css'"
"build-css": "sass static/sass/styles.scss static/css/styles.css --load-path=node_modules --style=compressed && postcss --map false --use autoprefixer --replace 'static/css/**/*.css'",
"lint-python": "flake8 --extend-ignore=E203 webapp && black --check --line-length 79 webapp",
"lint-scss": "stylelint static/**/*.scss",
"lint-js": "prettier -c 'static/js/*.{js,jsx,ts,tsx}'",
"format-python": "black --line-length 79 webapp",
"format-scss": "prettier -w 'static/sass/*.scss'",
"format-js": "prettier -w 'static/js/*.{js,jsx,ts,tsx}'",
"format-prettier": "yarn run format-scss && yarn run format-js"
},
"repository": {
"type": "git",
@ -12,10 +23,18 @@
"author": "canonical",
"license": "ISC",
"dependencies": {
"sass": "1.70.0",
"postcss": "8.4.31",
"autoprefixer": "10.4.13",
"global": "^4.4.0",
"postcss": "8.4.31",
"postcss-cli": "10.1.0",
"sass": "1.70.0",
"vanilla-framework": "4.16.0"
},
"devDependencies": {
"concurrently": "7.6.0",
"prettier": "3.4.1",
"stylelint": "16.11.0",
"stylelint-config-standard-scss": "13.1.0",
"watch-cli": "0.2.3"
}
}
}

View File

@ -1,39 +1,47 @@
const initNavigationSliding = () => {
const ANIMATION_SNAP_DURATION = 100;
const navigation = document.querySelector('.p-navigation--sliding, .p-navigation--reduced');
const toggles = document.querySelectorAll('.p-navigation__nav .p-navigation__link[aria-controls]:not(.js-back-button)');
const menuButton = document.querySelector('.js-menu-button');
const dropdownNavLists = document.querySelectorAll('.js-dropdown-nav-list');
const topNavList = [...dropdownNavLists].filter((list) => !list.parentNode.closest('.js-dropdown-nav-list'))[0];
const navigation = document.querySelector(
".p-navigation--sliding, .p-navigation--reduced",
);
const toggles = document.querySelectorAll(
".p-navigation__nav .p-navigation__link[aria-controls]:not(.js-back-button)",
);
const menuButton = document.querySelector(".js-menu-button");
const dropdownNavLists = document.querySelectorAll(".js-dropdown-nav-list");
const topNavList = [...dropdownNavLists].filter(
(list) => !list.parentNode.closest(".js-dropdown-nav-list"),
)[0];
const closeAllDropdowns = () => {
resetToggles();
navigation.classList.remove('has-menu-open');
menuButton.innerHTML = 'Menu';
navigation.classList.remove("has-menu-open");
menuButton.innerHTML = "Menu";
};
const keyPressHandler = (e) => {
if (e.key === 'Escape') {
if (e.key === "Escape") {
closeAllDropdowns();
document.removeEventListener('keydown', keyPressHandler);
document.removeEventListener("keydown", keyPressHandler);
}
};
menuButton.addEventListener('click', function(e) {
menuButton.addEventListener("click", function (e) {
e.preventDefault();
if (navigation.classList.contains('has-menu-open')) {
if (navigation.classList.contains("has-menu-open")) {
closeAllDropdowns();
} else {
navigation.classList.add('has-menu-open');
e.target.innerHTML = 'Close menu';
navigation.classList.add("has-menu-open");
e.target.innerHTML = "Close menu";
setFocusable(topNavList);
document.addEventListener('keydown', keyPressHandler);
document.addEventListener("keydown", keyPressHandler);
}
});
const resetToggles = (exception) => {
toggles.forEach(function(toggle) {
const target = document.getElementById(toggle.getAttribute('aria-controls'));
toggles.forEach(function (toggle) {
const target = document.getElementById(
toggle.getAttribute("aria-controls"),
);
if (!target || target === exception) {
return;
}
@ -43,21 +51,29 @@ const initNavigationSliding = () => {
const setActiveDropdown = (dropdownToggleButton, isActive = true) => {
// set active state of the dropdown toggle (to slide the panel into view)
const dropdownToggleEl = dropdownToggleButton.closest('.js-navigation-dropdown-toggle');
dropdownToggleEl?.classList.toggle('is-active', isActive);
const dropdownToggleEl = dropdownToggleButton.closest(
".js-navigation-dropdown-toggle",
);
dropdownToggleEl?.classList.toggle("is-active", isActive);
// set active state of the parent dropdown panel (to fade it out of view)
const parentLevelDropdown = dropdownToggleEl.closest('.js-navigation-sliding-panel');
parentLevelDropdown?.classList.toggle('is-active', isActive);
const parentLevelDropdown = dropdownToggleEl.closest(
".js-navigation-sliding-panel",
);
parentLevelDropdown?.classList.toggle("is-active", isActive);
};
const collapseDropdown = (dropdownToggleButton, targetDropdown, animated = false) => {
const collapseDropdown = (
dropdownToggleButton,
targetDropdown,
animated = false,
) => {
const closeHandler = () => {
targetDropdown.setAttribute('aria-hidden', 'true');
targetDropdown.setAttribute("aria-hidden", "true");
setActiveDropdown(dropdownToggleButton, false);
};
targetDropdown.classList.add('is-collapsed');
targetDropdown.classList.add("is-collapsed");
if (animated) {
setTimeout(closeHandler, ANIMATION_SNAP_DURATION);
} else {
@ -65,28 +81,36 @@ const initNavigationSliding = () => {
}
};
const expandDropdown = (dropdownToggleButton, targetDropdown, animated = false) => {
const expandDropdown = (
dropdownToggleButton,
targetDropdown,
animated = false,
) => {
setActiveDropdown(dropdownToggleButton);
targetDropdown.setAttribute('aria-hidden', 'false');
targetDropdown.setAttribute("aria-hidden", "false");
if (animated) {
// trigger the CSS transition
requestAnimationFrame(() => {
targetDropdown.classList.remove('is-collapsed');
targetDropdown.classList.remove("is-collapsed");
});
} else {
// make it appear immediately
targetDropdown.classList.remove('is-collapsed');
targetDropdown.classList.remove("is-collapsed");
}
setFocusable(targetDropdown);
};
// when clicking outside navigation, close all dropdowns
document.addEventListener('click', function(event) {
document.addEventListener("click", function (event) {
const target = event.target;
if (target.closest) {
if (!target.closest('.p-navigation, .p-navigation--sliding, .p-navigation--reduced')) {
if (
!target.closest(
".p-navigation, .p-navigation--sliding, .p-navigation--reduced",
)
) {
closeAllDropdowns();
}
}
@ -96,52 +120,60 @@ const initNavigationSliding = () => {
// turn on focusability for all direct children in the target dropdown
if (list) {
for (const item of list.children) {
item.children[0].setAttribute('tabindex', '0');
item.children[0].setAttribute("tabindex", "0");
}
}
};
const setFocusable = (target) => {
// turn off focusability for all dropdown lists in the navigation
dropdownNavLists.forEach(function(list) {
dropdownNavLists.forEach(function (list) {
if (list != topNavList) {
const elements = list.querySelectorAll('ul > li > a, ul > li > button');
elements.forEach(function(element) {
element.setAttribute('tabindex', '-1');
const elements = list.querySelectorAll("ul > li > a, ul > li > button");
elements.forEach(function (element) {
element.setAttribute("tabindex", "-1");
});
}
});
// if target dropdown is not a list, find the list in it
const isList = target.classList.contains('js-dropdown-nav-list');
const isList = target.classList.contains("js-dropdown-nav-list");
if (!isList) {
// find all lists in the target dropdown and make them focusable
target.querySelectorAll('.js-dropdown-nav-list').forEach(function(element) {
setListFocusable(element);
});
target
.querySelectorAll(".js-dropdown-nav-list")
.forEach(function (element) {
setListFocusable(element);
});
} else {
setListFocusable(target);
}
};
toggles.forEach(function(toggle) {
toggle.addEventListener('click', function(e) {
toggles.forEach(function (toggle) {
toggle.addEventListener("click", function (e) {
e.preventDefault();
const target = document.getElementById(toggle.getAttribute('aria-controls'));
const target = document.getElementById(
toggle.getAttribute("aria-controls"),
);
if (target) {
// check if the toggled dropdown is child of another dropdown
const isNested = !!target.parentNode.closest('.p-navigation__dropdown');
const isNested = !!target.parentNode.closest(".p-navigation__dropdown");
if (!isNested) {
resetToggles(target);
}
if (target.getAttribute('aria-hidden') === 'true') {
if (target.getAttribute("aria-hidden") === "true") {
// only animate the dropdown if menu is not open, otherwise just switch the visible one
expandDropdown(toggle, target, !navigation.classList.contains('has-menu-open'));
navigation.classList.add('has-menu-open');
expandDropdown(
toggle,
target,
!navigation.classList.contains("has-menu-open"),
);
navigation.classList.add("has-menu-open");
} else {
collapseDropdown(toggle, target, true);
navigation.classList.remove('has-menu-open');
navigation.classList.remove("has-menu-open");
}
}
});
@ -149,37 +181,41 @@ const initNavigationSliding = () => {
const goBackOneLevel = (e, backButton) => {
e.preventDefault();
const target = backButton.closest('.p-navigation__dropdown');
target.setAttribute('aria-hidden', 'true');
const target = backButton.closest(".p-navigation__dropdown");
target.setAttribute("aria-hidden", "true");
setActiveDropdown(backButton, false);
setFocusable(target.parentNode.parentNode);
};
dropdownNavLists.forEach(function(dropdown) {
dropdown.children[1].addEventListener('keydown', function(e) {
if (e.shiftKey && e.key === 'Tab' && window.getComputedStyle(dropdown.children[0], null).display === 'none') {
dropdownNavLists.forEach(function (dropdown) {
dropdown.children[1].addEventListener("keydown", function (e) {
if (
e.shiftKey &&
e.key === "Tab" &&
window.getComputedStyle(dropdown.children[0], null).display === "none"
) {
goBackOneLevel(e, dropdown.children[1].children[0]);
dropdown.parentNode.children[0].focus({
preventScroll: true
preventScroll: true,
});
}
});
});
document.querySelectorAll('.js-back-button').forEach(function(backButton) {
backButton.addEventListener('click', function(e) {
document.querySelectorAll(".js-back-button").forEach(function (backButton) {
backButton.addEventListener("click", function (e) {
goBackOneLevel(e, backButton);
});
});
// throttle util (for window resize event)
var throttle = function(fn, delay) {
var throttle = function (fn, delay) {
var timer = null;
return function() {
return function () {
var context = this,
args = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
@ -188,8 +224,8 @@ const initNavigationSliding = () => {
// hide side navigation drawer when screen is resized horizontally
let previousWidth = window.innerWidth;
window.addEventListener(
'resize',
throttle(function() {
"resize",
throttle(function () {
const currentWidth = window.innerWidth;
if (currentWidth !== previousWidth) {
closeAllDropdowns();
@ -199,4 +235,4 @@ const initNavigationSliding = () => {
);
};
initNavigationSliding();
initNavigationSliding();

View File

@ -1,9 +1,9 @@
// Import the framework
@import 'vanilla-framework';
@import "vanilla-framework";
// Include all of Vanilla Framework
@include vanilla;
// Blockquote style override until officially in vanilla
.p-pull-quote {
border-left: 3px solid #e95420;
@ -14,7 +14,7 @@
&:first-of-type::before,
&:last-of-type::after {
content: '';
content: "";
}
}
@ -23,42 +23,3 @@
margin-bottom: 0;
}
}
// Border for 2/3 images as per design
.p-image-container--2-3,
.is-underlined {
border-bottom: 3px solid #000;
}
// Brand font styling
.font-tester__demo {
&[data-family='monospace'] {
font-family: "Ubuntu Mono variable";
}
&[data-style='italic'] {
font-style: italic;
}
}
// Blog post needs to be cover, not contain
.blog-p-card--post .p-image-container__image {
-o-object-fit: cover;
object-fit: cover;
width: 100%;
height: 100%;
}
// Hero section styling
#ds_hero1 {
width: 100%;
bottom: 0;
position: absolute;
}
@media (max-width: 619px) {
#ds_hero1 {
display: none;
}
}

View File

@ -10,4 +10,4 @@ app = FlaskBase(
template_finder_view = TemplateFinder.as_view("template_finder")
app.add_url_rule("/", view_func=template_finder_view)
app.add_url_rule("/<path:subpath>", view_func=template_finder_view)
app.add_url_rule("/<path:subpath>", view_func=template_finder_view)

796
yarn.lock

File diff suppressed because it is too large Load Diff