Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into parker/add-message-icons
Browse files Browse the repository at this point in the history
  • Loading branch information
Parker-Stafford committed Oct 18, 2024
2 parents 63461ba + c41958b commit 4456c90
Show file tree
Hide file tree
Showing 58 changed files with 577 additions and 21,269 deletions.
9 changes: 1 addition & 8 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,10 @@ module.exports = {
sourceType: 'module',
impliedStrict: true,
},
plugins: [
'@typescript-eslint',
'react',
'import',
// Not strictly needed but fixes un-used imports via --fix
'unused-imports',
],
plugins: ['@typescript-eslint', 'react', 'import'],
rules: {
'import/order': 'error',
'no-unused-vars': 'off',
'unused-imports/no-unused-imports': 'error',
/**
* ignore if the variable starts with an underscore
*/
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:

- name: Install deps and build (with cache)
uses: bahmutov/npm-install@v1
with:
useLockFile: false

- name: Lint
run: yarn lint
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules
.cache
dist
.vscode
yarn.lock
7 changes: 1 addition & 6 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
module.exports = {
stories: ['../stories/**/*.stories.@(ts|tsx|js|jsx|mdx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-a11y',
'storybook-addon-designs',
],
addons: [],
// https://storybook.js.org/docs/react/configure/typescript#mainjs-configuration
typescript: {
check: true, // type-check stories during Storybook build
Expand Down
5,723 changes: 0 additions & 5,723 deletions example/yarn.lock

This file was deleted.

19 changes: 3 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.6.0",
"version": "1.8.7",
"license": "MIT",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down Expand Up @@ -28,11 +28,6 @@
"react": ">=18",
"react-dom": ">=18"
},
"husky": {
"hooks": {
"pre-commit": "tsdx lint"
}
},
"prettier": {
"printWidth": 80,
"semi": true,
Expand Down Expand Up @@ -71,12 +66,6 @@
"@emotion/babel-plugin": "^11.10.5",
"@emotion/babel-preset-css-prop": "^11.10.0",
"@size-limit/preset-small-lib": "^8.1.0",
"@storybook/addon-a11y": "^6.5.15",
"@storybook/addon-essentials": "^6.5.15",
"@storybook/addon-info": "^5.3.21",
"@storybook/addon-links": "^6.5.15",
"@storybook/addon-measure": "^6.5.10",
"@storybook/addons": "^6.5.15",
"@storybook/react": "^6.5.15",
"@types/react": "18",
"@types/react-dom": "18",
Expand All @@ -89,15 +78,12 @@
"eslint": "7.17.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-unused-imports": "^3.2.0",
"husky": "^8.0.1",
"polished": "^4.2.2",
"react": "18",
"react-dom": "18",
"react-hook-form": "^7.27.1",
"react-is": "^18.2.0",
"size-limit": "^8.1.0",
"storybook-addon-designs": "^6.3.1",
"ts-dedent": "^2.2.0",
"tsdx": "^0.14.1",
"tslib": "^2.0.3",
Expand All @@ -108,7 +94,8 @@
"react-dom": "^18",
"**/@typescript-eslint/eslint-plugin": "^4.1.1",
"**/@typescript-eslint/parser": "^4.1.1",
"eslint": "7.17.0"
"eslint": "7.17.0",
"braces": "^3.0.3"
},
"dependencies": {
"@emotion/react": "^11.10.5",
Expand Down
174 changes: 123 additions & 51 deletions src/accordion/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import React, { useState, ReactNode } from 'react';
import React, { useState, ReactNode, PropsWithChildren } from 'react';
import { css } from '@emotion/react';
import { Heading } from '../content';
import { Icon, ArrowIosDownwardOutline } from '../icon';
import { classNames } from '../utils/classNames';
import theme from '../theme';
import { AccordionContext, useAccordionContext } from './context';

export interface AccordionProps {
children: ReactNode;
size?: 'M' | 'L';
arrowPosition?: 'start' | 'end';
}

const sizeMap: Record<NonNullable<AccordionProps['size']>, string> = {
M: 'medium',
L: 'large',
};

const accordionItemCSS = css`
cursor: pointer;
padding: var(--accordion-padding-top) var(--accordion-padding-side);
height: var(--ac-accordion-item-height);
padding: 0 var(--accordion-padding-side);
display: block;
width: 100%;
display: flex;
Expand All @@ -25,44 +34,47 @@ const accordionItemCSS = css`
text-align: start;
color: var(--ac-global-text-color-900);
border-bottom: 1px solid var(--ac-global-border-color-dark);
/* remove outline - TODO might need to give a visual cue that this area is in focus */
outline: none;
background-color: var(--ac-global-background-color-light);
transition: background-color 0.2s ease-in-out;
transition: background-color ease-in-out 0.2s;
box-sizing: border-box;
&:hover {
background-color: var(--ac-global-background-color-light-hover);
}
.ac-accordion-item__title {
font-size: var(--accordion-font-size);
background-color: var(--ac-global-background-color-light);
}
`;

/**
* Accordion component for having collapsible sections
* @see https://www.w3.org/TR/wai-aria-practices-1.1/#accordion
*/
export function Accordion({ children }: AccordionProps) {
export function Accordion({ children, ...props }: AccordionProps) {
return (
<div
className={`ac-accordion ac-accordion--default`}
role="region"
css={css`
--accordion-animation-duration: ${theme.animation.global.duration}ms;
&.ac-accordion--default {
--accordion-padding-top: var(--ac-global-dimension-static-size-100);
--accordion-padding-side: var(--ac-global-dimension-static-size-200);
--accordion-font-size: ${theme.typography.sizes.medium.fontSize}px;
}
`}
>
{children}
</div>
<AccordionContext.Provider value={props}>
<div
className={`ac-accordion ac-accordion--default`}
role="region"
css={css`
--accordion-animation-duration: ${theme.animation.global.duration}ms;
&.ac-accordion--default {
--accordion-padding-top: var(--ac-global-dimension-static-size-100);
--accordion-padding-side: var(
--ac-global-dimension-static-size-200
);
--accordion-font-size: ${theme.typography.sizes.medium.fontSize}px;
}
.ac-accordion-item:not(:last-of-type) {
.ac-accordion-itemContent {
border-bottom: 1px solid var(--ac-global-border-color-dark);
}
}
`}
>
{children}
</div>
</AccordionContext.Provider>
);
}

export interface AccordionItemProps {
title: string;
title: ReactNode;
/**
* An extra interactive element to be displayed next to the header
*/
Expand All @@ -77,6 +89,10 @@ export interface AccordionItemProps {
* Callback function for when the collapsed state changes
*/
onChange?: (isOpen: boolean) => void;
/**
* An extra element to show on the right hand side
*/
extra?: ReactNode;
}

export function AccordionItem(props: AccordionItemProps) {
Expand All @@ -87,7 +103,10 @@ export function AccordionItem(props: AccordionItemProps) {
defaultIsOpen = true,
onChange,
children,
extra,
} = props;
const { arrowPosition = 'end', size = 'M' } = useAccordionContext();
const sizeVariant = sizeMap[size];
const [isOpen, setIsOpen] = useState(defaultIsOpen);
const contentId = `${id}-content`,
headerId = `${id}-heading`;
Expand All @@ -98,6 +117,8 @@ export function AccordionItem(props: AccordionItemProps) {
css={css`
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.ac-accordion-item__title {
margin-right: var(--ac-global-dimension-static-size-50);
}
Expand All @@ -114,6 +135,7 @@ export function AccordionItem(props: AccordionItemProps) {
<div
className={classNames('ac-accordion-item', {
'is-open': isOpen,
[`ac-accordion-item--${sizeVariant}`]: sizeVariant,
})}
role="presentation"
css={css`
Expand All @@ -122,38 +144,48 @@ export function AccordionItem(props: AccordionItemProps) {
transform: rotate(180deg);
}
}
&.ac-accordion-item--medium {
--ac-accordion-item-height: 40px;
}
&.ac-accordion-item--large {
--ac-accordion-item-height: 48px;
}
`}
>
<Heading level={3}>
<button
id={headerId}
css={accordionItemCSS}
onClick={() => {
const newIsOpen = !isOpen;
setIsOpen(newIsOpen);
onChange && onChange(newIsOpen);
}}
aria-controls={contentId}
aria-expanded={isOpen}
<div
role="button"
id={headerId}
css={accordionItemCSS}
onClick={() => {
const newIsOpen = !isOpen;
setIsOpen(newIsOpen);
onChange && onChange(newIsOpen);
}}
aria-controls={contentId}
aria-expanded={isOpen}
>
<FlexRow>
{arrowPosition === 'start' && <ArrowIcon />}
<Heading level={3}>{titleEl}</Heading>
</FlexRow>
<div
css={css`
display: flex;
flex-direction: row;
align-items: center;
gap: var(--ac-global-dimension-static-size-100);
`}
>
{titleEl}
<Icon
svg={<ArrowIosDownwardOutline />}
className="ac-accordion-itemIndicator"
css={css`
transition: transform ease var(--accordion-animation-duration);
transform: rotate(0deg);
`}
aria-hidden={true}
/>
</button>
</Heading>
{extra && <StopEventPropagation>{extra}</StopEventPropagation>}
{arrowPosition === 'end' && <ArrowIcon />}
</div>
</div>

<div
className="ac-accordion-itemContent"
id={contentId}
role="region"
css={css`
border-bottom: 1px solid var(--ac-global-border-color-dark);
display: ${isOpen ? 'block' : 'none'};
`}
aria-labelledby={headerId}
Expand All @@ -164,3 +196,43 @@ export function AccordionItem(props: AccordionItemProps) {
</div>
);
}

/**
* A wrapper component that stops the event from propagating up the DOM tree
*/
function StopEventPropagation(props: PropsWithChildren) {
return (
<div
onClick={e => {
e.stopPropagation();
}}
>
{props.children}
</div>
);
}

function ArrowIcon() {
return (
<Icon
svg={<ArrowIosDownwardOutline />}
className="ac-accordion-itemIndicator"
css={css`
transition: transform ease var(--accordion-animation-duration);
transform: rotate(0deg);
`}
aria-hidden={true}
/>
);
}

const flexRowCSS = css`
display: flex;
flex-direction: row;
align-items: center;
gap: var(--ac-global-dimension-static-size-100);
`;

function FlexRow(props: PropsWithChildren) {
return <div css={flexRowCSS}>{props.children}</div>;
}
23 changes: 23 additions & 0 deletions src/accordion/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createContext, useContext } from 'react';
import { AccordionProps } from './Accordion';

interface AccordionContextType {
/**
* The position of the arrow icon in the accordion item
* @default 'end'
*/
arrowPosition?: AccordionProps['arrowPosition'];
/**
* The size of the accordion item
* @default 'M'
*/
size?: AccordionProps['size'];
}
export const AccordionContext = createContext<AccordionContextType>({
arrowPosition: 'end',
size: 'M',
});

export function useAccordionContext(): AccordionContextType {
return useContext(AccordionContext);
}
Loading

0 comments on commit 4456c90

Please sign in to comment.