mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat: use component library's dropdown component in learn (#50465)
Co-authored-by: Sboonny <muhammed@freecodecamp.org> Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Bruce Blaser <bbsmooth@gmail.com> Co-authored-by: Naomi Carrigan <nhcarrigan@gmail.com>
This commit is contained in:
+2
-1
@@ -1,5 +1,5 @@
|
||||
client/.cache/**
|
||||
client/static/**
|
||||
client/.cache/**
|
||||
client/public/**
|
||||
api-server/src/public/**
|
||||
api-server/lib/**
|
||||
@@ -10,3 +10,4 @@ config/donation-settings.js
|
||||
config/superblocks.js
|
||||
web/**
|
||||
docs/**/*.md
|
||||
tools/ui-components/dist/**
|
||||
@@ -22,3 +22,4 @@ web/.next
|
||||
curriculum-server/data/curriculum.json
|
||||
docs/**/*.md
|
||||
client/src/components/Donation/types.js
|
||||
tools/ui-components/dist
|
||||
+4
-2
@@ -19,11 +19,12 @@
|
||||
"author": "freeCodeCamp <team@freecodecamp.org>",
|
||||
"main": "none",
|
||||
"scripts": {
|
||||
"prebuild": "pnpm -w run create:config && pnpm run build:workers --env production",
|
||||
"prebuild": "pnpm -w run create:config && pnpm run build:workers --env production && pnpm run build:components-library",
|
||||
"build": "cross-env NODE_OPTIONS=\"--max-old-space-size=7168\" gatsby build --prefix-paths",
|
||||
"build:workers": "cross-env NODE_OPTIONS=\"--max-old-space-size=7168\" webpack --config ./webpack-workers.js",
|
||||
"clean": "gatsby clean",
|
||||
"predevelop": "pnpm run build:workers --env development",
|
||||
"predevelop": "pnpm -w run create:config && pnpm run build:workers --env development && pnpm run build:components-library",
|
||||
"build:components-library": "pnpm run -F=@freecodecamp/ui build",
|
||||
"develop": "cross-env NODE_OPTIONS=\"--max-old-space-size=5000\" gatsby develop --inspect=9230",
|
||||
"lint": "ts-node ./i18n/schema-validation.ts",
|
||||
"serve": "gatsby serve -p 8000",
|
||||
@@ -49,6 +50,7 @@
|
||||
"@freecodecamp/react-bootstrap": "0.32.3",
|
||||
"@freecodecamp/react-calendar-heatmap": "1.1.0",
|
||||
"@freecodecamp/strip-comments": "3.0.1",
|
||||
"@freecodecamp/ui": "workspace:*",
|
||||
"@growthbook/growthbook-react": "0.16.0",
|
||||
"@loadable/component": "5.15.3",
|
||||
"@reach/router": "1.3.4",
|
||||
|
||||
@@ -65,22 +65,6 @@ describe('<TimeLine />', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Render button when both githubLink and solution is present', () => {
|
||||
// @ts-expect-error
|
||||
render(<TimeLine {...propsForOnlySolution} />, store);
|
||||
|
||||
const menuItems = screen.getAllByRole('menuitem');
|
||||
expect(menuItems).toHaveLength(2);
|
||||
expect(menuItems[0]).toHaveAttribute(
|
||||
'href',
|
||||
'https://github.com/freeCodeCamp/freeCodeCamp1'
|
||||
);
|
||||
expect(menuItems[1]).toHaveAttribute(
|
||||
'href',
|
||||
'https://github.com/freeCodeCamp/freeCodeCamp2'
|
||||
);
|
||||
});
|
||||
|
||||
it('rendering the correct button when files is present', () => {
|
||||
// @ts-expect-error
|
||||
render(<TimeLine {...propsForOnlySolution} />, store);
|
||||
|
||||
@@ -72,37 +72,6 @@ describe('<certification />', () => {
|
||||
})
|
||||
).toHaveAttribute('href', 'https://github.com/freeCodeCamp/freeCodeCamp');
|
||||
});
|
||||
|
||||
it('Render button when both githubLink and solution is present', () => {
|
||||
renderWithRedux(<CertificationSettings {...propsForOnlySolution} />);
|
||||
|
||||
const links = screen.getAllByRole('menuitem');
|
||||
expect(links[0]).toHaveAttribute(
|
||||
'href',
|
||||
'https://github.com/freeCodeCamp/freeCodeCamp1'
|
||||
);
|
||||
|
||||
expect(links[1]).toHaveAttribute(
|
||||
'href',
|
||||
'https://github.com/freeCodeCamp/freeCodeCamp2'
|
||||
);
|
||||
});
|
||||
|
||||
it('rendering the correct button when files is present', () => {
|
||||
renderWithRedux(<CertificationSettings {...propsForMultifileProject} />);
|
||||
|
||||
expect(
|
||||
screen.getByRole('menuitem', {
|
||||
name: 'buttons.view-code'
|
||||
})
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.getByRole('menuitem', {
|
||||
name: 'buttons.view-project'
|
||||
})
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
const defaultTestProps = {
|
||||
@@ -351,23 +320,3 @@ const propsForOnlySolution = {
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const propsForMultifileProject = {
|
||||
...defaultTestProps,
|
||||
completedChallenges: [
|
||||
{
|
||||
id: '587d78af367417b2b2512b03',
|
||||
completedDate: 123456789,
|
||||
challengeFiles: [
|
||||
{
|
||||
contents,
|
||||
ext,
|
||||
fileKey,
|
||||
name,
|
||||
path
|
||||
}
|
||||
],
|
||||
challengeType: 14
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Button, Dropdown, MenuItem } from '@freecodecamp/react-bootstrap';
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import { Dropdown, MenuItem } from '@freecodecamp/ui';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CompletedChallenge } from '../../redux/prop-types';
|
||||
import { getSolutionDisplayType } from '../../utils/solution-display-type';
|
||||
import './solution-display-widget.css';
|
||||
import '@freecodecamp/ui/dist/base.css';
|
||||
interface Props {
|
||||
completedChallenge: CompletedChallenge;
|
||||
dataCy?: string;
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
.tool-panel-group-mobile > .dropdown > .btn-block {
|
||||
margin: 0 2px 0 0;
|
||||
padding: 5px 0;
|
||||
height: 37px;
|
||||
}
|
||||
|
||||
.tool-panel-group .btn-group {
|
||||
@@ -37,7 +36,11 @@
|
||||
}
|
||||
|
||||
.tool-panel-group-mobile .btn {
|
||||
margin-bottom: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.tool-panel-group-mobile *:focus-visible {
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
.tool-panel-group .dropdown-menu {
|
||||
@@ -52,7 +55,3 @@
|
||||
.tool-panel-group .dropdown-menu a {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
#get-help-dropdown > .caret {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {
|
||||
Button,
|
||||
DropdownButton,
|
||||
MenuItem
|
||||
} from '@freecodecamp/react-bootstrap';
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import { Dropdown, MenuItem } from '@freecodecamp/ui';
|
||||
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
@@ -104,40 +103,28 @@ function ToolPanel({
|
||||
{isMobile ? t('buttons.reset') : t('buttons.reset-lesson')}
|
||||
</Button>
|
||||
)}
|
||||
<DropdownButton
|
||||
block={true}
|
||||
bsStyle='primary'
|
||||
className='btn-invert'
|
||||
id='get-help-dropdown'
|
||||
title={isMobile ? t('buttons.help') : t('buttons.get-help')}
|
||||
>
|
||||
{guideUrl ? (
|
||||
<MenuItem
|
||||
bsStyle='primary'
|
||||
className='btn-invert'
|
||||
href={guideUrl}
|
||||
target='_blank'
|
||||
>
|
||||
{t('buttons.get-hint')}
|
||||
<Dropdown dropup>
|
||||
<Dropdown.Toggle dropup id={'get-help-dropdown'}>
|
||||
{isMobile ? t('buttons.help') : t('buttons.get-help')}
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu dropup>
|
||||
{guideUrl ? (
|
||||
<MenuItem href={guideUrl} target='_blank'>
|
||||
{t('buttons.get-hint')}{' '}
|
||||
<FontAwesomeIcon icon={faExternalLinkAlt} />
|
||||
<span className='sr-only'>, {t('aria.opens-new-window')}</span>
|
||||
</MenuItem>
|
||||
) : null}
|
||||
{videoUrl ? (
|
||||
<MenuItem onClick={openVideoModal}>
|
||||
{t('buttons.watch-video')}
|
||||
</MenuItem>
|
||||
) : null}
|
||||
<MenuItem onClick={openHelpModal}>
|
||||
{t('buttons.ask-for-help')}
|
||||
</MenuItem>
|
||||
) : null}
|
||||
{videoUrl ? (
|
||||
<MenuItem
|
||||
bsStyle='primary'
|
||||
className='btn-invert'
|
||||
onClick={openVideoModal}
|
||||
>
|
||||
{t('buttons.watch-video')}
|
||||
</MenuItem>
|
||||
) : null}
|
||||
<MenuItem
|
||||
bsStyle='primary'
|
||||
className='btn-invert'
|
||||
onClick={openHelpModal}
|
||||
>
|
||||
{t('buttons.ask-for-help')}
|
||||
</MenuItem>
|
||||
</DropdownButton>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/* Don't delete! The empty config is needed when building the client */
|
||||
@@ -8,17 +8,19 @@ describe('Help Button', () => {
|
||||
|
||||
it('should toggle the dropdown menu', () => {
|
||||
cy.get('#get-help-dropdown').scrollIntoView().click();
|
||||
cy.get('.tool-panel-group ul[role="menu"]')
|
||||
cy.get('.tool-panel-group [role="menu"]')
|
||||
.scrollIntoView()
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
it('should render three links when video is available', () => {
|
||||
cy.get('.tool-panel-group ul[role="menu"]').within(() => {
|
||||
cy.get('a').should('have.length', 3);
|
||||
cy.get('a').eq(0).contains('Get a Hint');
|
||||
cy.get('a').eq(1).contains('Watch a Video');
|
||||
cy.get('a').eq(2).contains('Ask for Help');
|
||||
cy.get('.tool-panel-group [role="menu"]')
|
||||
.children()
|
||||
.should('have.length', 3);
|
||||
cy.get('.tool-panel-group [role="menu"]').within(() => {
|
||||
cy.get('a').contains('Get a Hint');
|
||||
cy.get('button').contains('Watch a Video');
|
||||
cy.get('button').contains('Ask for Help');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,10 +29,12 @@ describe('Help Button', () => {
|
||||
'/learn/front-end-development-libraries/bootstrap/apply-the-default-bootstrap-button-style'
|
||||
);
|
||||
cy.get('#get-help-dropdown').scrollIntoView().click();
|
||||
cy.get('.tool-panel-group ul[role="menu"]').within(() => {
|
||||
cy.get('a').should('have.length', 2);
|
||||
cy.get('a').eq(0).contains('Get a Hint');
|
||||
cy.get('a').eq(1).contains('Ask for Help');
|
||||
cy.get('.tool-panel-group [role="menu"]')
|
||||
.children()
|
||||
.should('have.length', 2);
|
||||
cy.get('.tool-panel-group [role="menu"]').within(() => {
|
||||
cy.get('a').contains('Get a Hint');
|
||||
cy.get('button').contains('Ask for Help');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Generated
+2141
-4114
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,6 @@
|
||||
/* the styled-elements and normalized are included here to replicate the presets that exist in the learn app */
|
||||
import '../src/normalize.css';
|
||||
import '../src/global-element-styles.css';
|
||||
import '../src/base.css';
|
||||
|
||||
export const parameters = {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"@headlessui/react": "1.7.15",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"tslib": "2.5.2",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -58,6 +59,7 @@
|
||||
"babel-loader": "8.3.0",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
|
||||
"cross-env": "7.0.3",
|
||||
"npm-run-all": "4.1.5",
|
||||
"postcss": "8.4.24",
|
||||
"postcss-import": "14.1.0",
|
||||
"rollup": "2.79.1",
|
||||
@@ -72,7 +74,9 @@
|
||||
"build": "pnpm run build:css && pnpm run build:js",
|
||||
"build:js": "cross-env NODE_ENV=production rollup -c",
|
||||
"build:css": "pnpm dlx tailwindcss -i ./src/base.css -o ./dist/base.css --minify",
|
||||
"dev": "cross-env NODE_ENV=development rollup -c -w",
|
||||
"dev:js": "cross-env NODE_ENV=development rollup -c -w ",
|
||||
"dev:css": "pnpm tailwindcss -i ./src/base.css -o ./dist/base.css --watch",
|
||||
"develop": "npm-run-all --parallel dev:css dev:js storybook",
|
||||
"clean": "rm -rf dist/*",
|
||||
"gen-component": "ts-node ./utils/gen-component-script"
|
||||
}
|
||||
|
||||
@@ -32,14 +32,19 @@ const DropUpChildren = () => (
|
||||
);
|
||||
|
||||
export const Menus = (): JSX.Element => (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
height: '220px',
|
||||
width: '220px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<Dropdown>
|
||||
<DropDownChildren />
|
||||
</Dropdown>
|
||||
<Dropdown>
|
||||
<DropDownChildren />
|
||||
</Dropdown>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
|
||||
const UpTemplate: Story<DropdownProps> = args => {
|
||||
@@ -47,14 +52,13 @@ const UpTemplate: Story<DropdownProps> = args => {
|
||||
<div
|
||||
style={{
|
||||
height: '220px',
|
||||
width: '220px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-end'
|
||||
}}
|
||||
>
|
||||
<div style={{ width: '220px' }}>
|
||||
<Dropdown {...args} />
|
||||
</div>
|
||||
<Dropdown {...args} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ describe('<DropDownButton>', () => {
|
||||
userEvent.click(dropdownTrigger);
|
||||
const unorderedList = screen.getByRole('menu');
|
||||
expect(unorderedList).toHaveClass(
|
||||
'shadow-lg bg-foreground-primary text-background-primary text-center ring-1 ring-black ring-opacity-5 focus:outline-transparent origin-top-right absolute min-w-full py-1 z-10 transform -translate-y-full top-0'
|
||||
'list-none bg-foreground-secondary text-center border-1 border-solid border-background-quaternary focus:outline-transparent origin-top-right absolute w-full min-w-max py-1 px-0 z-10 transform -translate-y-full top-0'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import React, { createContext, useContext, useRef } from 'react';
|
||||
import { Menu } from '@headlessui/react';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faCaretDown, faCaretUp } from '@fortawesome/free-solid-svg-icons';
|
||||
@@ -11,24 +11,33 @@ export type DropdownProps = React.ComponentPropsWithoutRef<typeof Menu> & {
|
||||
|
||||
type DropDownButtonProps = React.ComponentPropsWithoutRef<typeof Menu>;
|
||||
|
||||
const DropDownContext = createContext<DropdownProps>({});
|
||||
type DropDownContextProps = DropdownProps & {
|
||||
menuButtonRef: React.MutableRefObject<HTMLButtonElement | null>;
|
||||
};
|
||||
const DropDownContext = createContext<DropDownContextProps>({
|
||||
menuButtonRef: React.createRef()
|
||||
});
|
||||
|
||||
const dropDownItems =
|
||||
'shadow-lg bg-foreground-primary text-background-primary text-center ring-1 ring-black ring-opacity-5 focus:outline-transparent origin-top-right absolute min-w-full py-1 z-10';
|
||||
'list-none bg-foreground-secondary text-center border-1 border-solid border-background-quaternary focus:outline-transparent origin-top-right absolute w-full min-w-max py-1 px-0 z-10';
|
||||
const dropUpItems = dropDownItems + ' transform -translate-y-full top-0';
|
||||
const toggleClassNames =
|
||||
'cursor-pointer border-3 text-center touch-manipulation bg-background-quaternary text-foreground-secondary px-3 py-1.5 relative hover:bg-foreground-primary hover:text-background-primary btn-block border-foreground-primary';
|
||||
'cursor-pointer border-3 border-solid w-full block text-center touch-manipulation bg-background-quaternary text-foreground-secondary px-3 py-1.5 relative hover:bg-foreground-secondary hover:text-background-secondary btn-block border-foreground-secondary';
|
||||
|
||||
export const MenuItems = React.forwardRef<
|
||||
React.ElementRef<typeof Menu.Items>,
|
||||
MenuItemsProps
|
||||
>(({ children, className }, ref) => {
|
||||
const { dropup } = useContext(DropDownContext);
|
||||
const { dropup, menuButtonRef } = useContext(DropDownContext);
|
||||
|
||||
const handleClick = () => {
|
||||
menuButtonRef.current?.focus();
|
||||
};
|
||||
|
||||
const itemsClasses = dropup ? dropUpItems : dropDownItems;
|
||||
const buttonClass: string = [className, itemsClasses].join(' ');
|
||||
return (
|
||||
<Menu.Items as='ul' className={buttonClass} ref={ref}>
|
||||
<Menu.Items className={buttonClass} ref={ref} onClick={handleClick}>
|
||||
{children}
|
||||
</Menu.Items>
|
||||
);
|
||||
@@ -39,15 +48,15 @@ const DropDownButton = ({
|
||||
className,
|
||||
...rest
|
||||
}: DropDownButtonProps) => {
|
||||
const { dropup } = useContext(DropDownContext);
|
||||
const { dropup, menuButtonRef } = useContext(DropDownContext);
|
||||
|
||||
const classes = [className, toggleClassNames].join(' ');
|
||||
return (
|
||||
<Menu.Button className={classes} {...rest}>
|
||||
<Menu.Button ref={menuButtonRef} className={classes} {...rest}>
|
||||
{children}
|
||||
<FontAwesomeIcon
|
||||
icon={dropup ? faCaretUp : faCaretDown}
|
||||
className='mt-2 ml-2 -mr-1 h-3 w-3 text-violet-200'
|
||||
className='mt-2 mx-2 h-3'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
</Menu.Button>
|
||||
@@ -59,9 +68,11 @@ export const Dropdown = ({
|
||||
dropup,
|
||||
...props
|
||||
}: DropdownProps): JSX.Element => {
|
||||
const menuButtonRef = useRef(null);
|
||||
const context = { dropup, menuButtonRef };
|
||||
return (
|
||||
<DropDownContext.Provider value={{ dropup }}>
|
||||
<Menu className='relative' as='div' {...props}>
|
||||
<DropDownContext.Provider value={context}>
|
||||
<Menu className='relative w-full' as='div' {...props}>
|
||||
{children}
|
||||
</Menu>
|
||||
</DropDownContext.Provider>
|
||||
|
||||
@@ -87,19 +87,26 @@ export const HeadlessButton = React.forwardRef<
|
||||
);
|
||||
|
||||
const defaultClass =
|
||||
'block text-center no-underline px-[20px] py-[3px] bg-foreground-primary text-background-primary bg-foreground-primary text-background-primary focus:bg-background-primary focus:text-foreground-primary hover:text-foreground-primary hover:bg-background-primary w-full';
|
||||
'block text-center no-underline border-none px-4 py-1.5 focus:bg-background-secondary focus:text-foreground-secondary hover:text-foreground-secondary hover:bg-background-secondary w-full';
|
||||
|
||||
export const MenuItem = ({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: MenuItemsProps): JSX.Element => {
|
||||
const classes = [defaultClass, className].join(' ');
|
||||
return (
|
||||
<Menu.Item as='li'>
|
||||
<HeadlessButton className={classes} {...props}>
|
||||
{children}
|
||||
</HeadlessButton>
|
||||
<Menu.Item>
|
||||
{({ active }) => {
|
||||
const activeStyles = active
|
||||
? 'text-foreground-secondary bg-background-secondary outline outline-3 outline-blue-500 outline-offset-[-3px]'
|
||||
: 'text-background-secondary bg-foreground-secondary';
|
||||
const classes = [defaultClass, className, activeStyles].join(' ');
|
||||
return (
|
||||
<HeadlessButton className={classes} {...props}>
|
||||
{children}
|
||||
</HeadlessButton>
|
||||
);
|
||||
}}
|
||||
</Menu.Item>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,457 @@
|
||||
/*!
|
||||
element styles of the minified boostrap 3 css file
|
||||
*/
|
||||
@media print {
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
background: transparent !important;
|
||||
color: #000 !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
a,
|
||||
a:visited {
|
||||
text-decoration: underline;
|
||||
}
|
||||
a[href]:after {
|
||||
content: ' (' attr(href) ')';
|
||||
}
|
||||
abbr[title]:after {
|
||||
content: ' (' attr(title) ')';
|
||||
}
|
||||
a[href^='#']:after,
|
||||
a[href^='javascript:']:after {
|
||||
content: '';
|
||||
}
|
||||
pre,
|
||||
blockquote {
|
||||
border: 1px solid #999;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
tr,
|
||||
img {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
p,
|
||||
h2,
|
||||
h3 {
|
||||
orphans: 3;
|
||||
widows: 3;
|
||||
}
|
||||
h2,
|
||||
h3 {
|
||||
page-break-after: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
*:before,
|
||||
*:after {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html {
|
||||
font-size: 10px;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
body {
|
||||
font-family: 'Lato', Helvetica, Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
line-height: 1.42857143;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
}
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
a {
|
||||
color: #006400;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover,
|
||||
a:focus {
|
||||
color: #001800;
|
||||
text-decoration: underline;
|
||||
}
|
||||
figure {
|
||||
margin: 0;
|
||||
}
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 25px;
|
||||
border: 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
.sr-only-focusable:active,
|
||||
.sr-only-focusable:focus {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
clip: auto;
|
||||
}
|
||||
[role='button'] {
|
||||
cursor: pointer;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: inherit;
|
||||
font-weight: 400;
|
||||
line-height: 1.1;
|
||||
color: inherit;
|
||||
}
|
||||
h1 small,
|
||||
h2 small,
|
||||
h3 small,
|
||||
h4 small,
|
||||
h5 small,
|
||||
h6 small {
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
color: #777;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 12.5px;
|
||||
}
|
||||
h1 small,
|
||||
h2 small,
|
||||
h3 small {
|
||||
font-size: 65%;
|
||||
}
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 12.5px;
|
||||
margin-bottom: 12.5px;
|
||||
}
|
||||
h4 small,
|
||||
h6 small {
|
||||
font-size: 75%;
|
||||
}
|
||||
h1 {
|
||||
font-size: 46px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 38px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 31px;
|
||||
}
|
||||
h4 {
|
||||
font-size: 23px;
|
||||
}
|
||||
h5 {
|
||||
font-size: 18px;
|
||||
}
|
||||
h6 {
|
||||
font-size: 16px;
|
||||
}
|
||||
p {
|
||||
margin: 0 0 12.5px;
|
||||
}
|
||||
small {
|
||||
font-size: 88%;
|
||||
}
|
||||
mark {
|
||||
background-color: #fcf8e3;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin-top: 0;
|
||||
margin-bottom: 12.5px;
|
||||
}
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol,
|
||||
ol ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
dt,
|
||||
dd {
|
||||
line-height: 1.42857143;
|
||||
}
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
dd {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
cursor: help;
|
||||
border-bottom: 1px dotted #777;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding: 12.5px 25px;
|
||||
margin: 0 0 25px;
|
||||
font-size: 22.5px;
|
||||
border-left: 5px solid #eee;
|
||||
}
|
||||
blockquote p:last-child,
|
||||
blockquote ul:last-child,
|
||||
blockquote ol:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
blockquote footer,
|
||||
blockquote small {
|
||||
display: block;
|
||||
font-size: 80%;
|
||||
line-height: 1.42857143;
|
||||
color: #777;
|
||||
}
|
||||
blockquote footer:before,
|
||||
blockquote small:before {
|
||||
content: '\2014 \00A0';
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 25px;
|
||||
font-style: normal;
|
||||
line-height: 1.42857143;
|
||||
}
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
code {
|
||||
padding: 2px 4px;
|
||||
font-size: 90%;
|
||||
color: #c7254e;
|
||||
background-color: #f9f2f4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
kbd {
|
||||
padding: 2px 4px;
|
||||
font-size: 90%;
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
border-radius: 3px;
|
||||
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 100%;
|
||||
font-weight: bold;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
pre {
|
||||
display: block;
|
||||
padding: 12px;
|
||||
margin: 0 0 12.5px;
|
||||
font-size: 17px;
|
||||
line-height: 1.42857143;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
color: #333;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
pre code {
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
white-space: pre-wrap;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
}
|
||||
fieldset {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 25px;
|
||||
font-size: 27px;
|
||||
line-height: inherit;
|
||||
color: #333;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
label {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
input[type='search'] {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
input[type='radio'],
|
||||
input[type='checkbox'] {
|
||||
margin: 4px 0 0;
|
||||
margin-top: 1px \9;
|
||||
line-height: normal;
|
||||
}
|
||||
input[type='file'] {
|
||||
display: block;
|
||||
}
|
||||
input[type='range'] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
select[multiple],
|
||||
select[size] {
|
||||
height: auto;
|
||||
}
|
||||
output {
|
||||
display: block;
|
||||
padding-top: 7px;
|
||||
font-size: 18px;
|
||||
line-height: 1.42857143;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
input[type='search'] {
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
input[type='radio'][disabled],
|
||||
input[type='checkbox'][disabled],
|
||||
fieldset[disabled] input[type='radio'],
|
||||
fieldset[disabled] input[type='checkbox'] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Element styles rewrites from global.css that could be moved to presets*/
|
||||
/* typography should be handled in tailwind config */
|
||||
html {
|
||||
height: 100%;
|
||||
font-size: 18px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
font-family: var(--font-family-sans-serif);
|
||||
color: var(--secondary-color);
|
||||
background: var(--secondary-background);
|
||||
--header-height: 38px;
|
||||
}
|
||||
|
||||
header {
|
||||
top: 0;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
input {
|
||||
outline-color: transparent;
|
||||
-webkit-box-shadow: none !important;
|
||||
-moz-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
code {
|
||||
border-radius: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
/* border-top-color: var(--quaternary-background); */
|
||||
}
|
||||
|
||||
code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
blockquote footer,
|
||||
blockquote small,
|
||||
blockquote .small {
|
||||
color: #858591;
|
||||
}
|
||||
|
||||
/* WCAG fix */
|
||||
.sr-only {
|
||||
color: white;
|
||||
background-color: black;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// Use this file as the entry point for component export
|
||||
export { Button } from './button';
|
||||
export { Alert } from './alert';
|
||||
export { Image } from './image';
|
||||
export { Table } from './table';
|
||||
export { ToggleButton } from './toggle-button';
|
||||
// export { Button } from './button';
|
||||
// export { Alert } from './alert';
|
||||
// export { Image } from './image';
|
||||
// export { Table } from './table';
|
||||
// export { ToggleButton } from './toggle-button';
|
||||
export { Dropdown } from './drop-down';
|
||||
export { MenuItem } from './drop-down/menu-item';
|
||||
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
menu,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
audio,
|
||||
canvas,
|
||||
progress,
|
||||
video {
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
[hidden],
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
mark {
|
||||
background: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
hr {
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
}
|
||||
pre {
|
||||
overflow: auto;
|
||||
}
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
button {
|
||||
overflow: visible;
|
||||
}
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
button,
|
||||
html input[type='button'],
|
||||
input[type='reset'],
|
||||
input[type='submit'] {
|
||||
-webkit-appearance: button;
|
||||
cursor: pointer;
|
||||
}
|
||||
button[disabled],
|
||||
html input[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
input {
|
||||
line-height: normal;
|
||||
}
|
||||
input[type='checkbox'],
|
||||
input[type='radio'] {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
input[type='number']::-webkit-inner-spin-button,
|
||||
input[type='number']::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
input[type='search'] {
|
||||
-webkit-appearance: textfield;
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
input[type='search']::-webkit-search-cancel-button,
|
||||
input[type='search']::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
legend {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
optgroup {
|
||||
font-weight: bold;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
td,
|
||||
th {
|
||||
padding: 0;
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
const plugin = require('tailwindcss/plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: 'jit',
|
||||
corePlugins: {
|
||||
preflight: false
|
||||
},
|
||||
content: [
|
||||
'./src/**/*.html',
|
||||
'./src/**/*.js',
|
||||
@@ -78,6 +82,9 @@ module.exports = {
|
||||
1: '1px',
|
||||
3: '3px'
|
||||
},
|
||||
outlineWidth: {
|
||||
3: '3px'
|
||||
},
|
||||
fontSize: {
|
||||
// https://tailwindcss.com/docs/font-size#providing-a-default-line-height
|
||||
// [fontSize, lineHeight]
|
||||
|
||||
Reference in New Issue
Block a user