Date: Thu, 8 Oct 2020 07:37:05 -0700
Subject: [PATCH 19/49] feat(side-panel): Refactor using hooks
Also changed prop name from collapsed to expanded to more closely align with aria-expanded.
---
modules/_labs/side-panel/react/index.ts | 3 +-
.../_labs/side-panel/react/lib/SidePanel.tsx | 182 +++++++++---------
modules/_labs/side-panel/react/lib/hooks.ts | 61 ++++--
.../side-panel/react/stories/stories.tsx | 117 ++++++++---
.../react/stories/stories_VisualTesting.tsx | 4 +-
5 files changed, 226 insertions(+), 141 deletions(-)
diff --git a/modules/_labs/side-panel/react/index.ts b/modules/_labs/side-panel/react/index.ts
index 3ccf10db9f..8a64323994 100644
--- a/modules/_labs/side-panel/react/index.ts
+++ b/modules/_labs/side-panel/react/index.ts
@@ -1,5 +1,6 @@
import SidePanel from './lib/SidePanel';
+import {useSidePanel} from './lib/hooks';
export default SidePanel;
-export {SidePanel};
+export {SidePanel, useSidePanel};
export * from './lib/SidePanel';
diff --git a/modules/_labs/side-panel/react/lib/SidePanel.tsx b/modules/_labs/side-panel/react/lib/SidePanel.tsx
index 3a9cb9dad6..4566e12d7f 100644
--- a/modules/_labs/side-panel/react/lib/SidePanel.tsx
+++ b/modules/_labs/side-panel/react/lib/SidePanel.tsx
@@ -1,75 +1,69 @@
/** @jsx jsx */
import * as React from 'react';
import {css, jsx, keyframes, CSSObject} from '@emotion/core';
-import {useUniqueId} from '@workday/canvas-kit-react-common';
-import {IconButton} from '@workday/canvas-kit-react-button';
+import {IconButton, IconButtonProps} from '@workday/canvas-kit-react-button';
import {spacing, colors, depth} from '@workday/canvas-kit-react-core';
+import {accessibleHide} from '@workday/canvas-kit-react-common';
import {transformationImportIcon} from '@workday/canvas-system-icons-web';
-import {useControllableState} from './hooks';
-
export type SidePanelVariant = 'standard' | 'alternate';
-export type SidePanelInternalStates = 'collapsed' | 'collapsing' | 'expanded' | 'expanding';
+export type SidePanelTransitionStates = 'collapsed' | 'collapsing' | 'expanded' | 'expanding';
export interface SidePanelProps extends React.HTMLAttributes {
/**
* The `aria-label` attribute for the side panel and button. This is required for accessibility.
*
*/
- 'aria-label': string;
+ 'aria-label'?: string;
/**
- * Specifies the _controlled_ state the side panel is in. Leave undefined if you want to keep this an _uncontrolled_ component.
- * Changing this prop will fire an animation that resolves to this state. For example, if collapsed is changed from `true` to `false`,
- * an expand animation will fire and the side panel will eventually be expanded when the animation is finished.
- *
- * @default false
- */
- collapsed?: boolean;
- /**
- * Specifies default state (collapsed or expanded) if the component is uncontrolled.
+ * The width of the component (in `px` if it's a `number`) when it is collapsed.
*
- * @default false
+ * @default 64
*/
- defaultCollapsed?: boolean;
+ collapsedWidth?: number | string;
/**
- * The height of the component (in `px` if it's a `number`).
- *
- * @default '100%'
+ * If true, sets the expanded state of the side panel
+ * @default true
*/
- height?: number | string;
+ expanded?: boolean;
/**
* The width of the component (in `px` if it's a `number`) when it is expanded.
*
* @default 320
*/
- width?: number | string;
+ expandedWidth?: number | string;
/**
- * The width of the component (in `px` if it's a `number`) when it is collapsed.
+ * The height of the component (in `px` if it's a `number`).
*
- * @default 64
+ * @default '100%'
*/
- collapsedWidth?: number | string;
+ height?: number | string;
/**
- * Specifies which side the side panel is meant to originate from.
+ * Which side the side panel is meant to originate from.
*
* @default 'left'
*/
origin?: 'left' | 'right';
/**
- * Fired when the side panel's `collapsed` state changes. States like 'collapsing' and 'expanding' are tracked in another callback: `onStateChange`
+ * InternalState
+ */
+ state?: SidePanelTransitionStates;
+ /**
+ * The function called when the side panel's `expanded` state changes. States like 'collapsing' and 'expanding' are tracked in another callback: `onStateChange`
*
* @param boolean
*/
- onCollapsedChange?: (collapsed?: boolean) => void;
+ onExpandedChange?: (expanded?: boolean) => void;
/**
- * Fired when the side panel is transitioning between internal states. Use this to track when the side panel is animating between 'collapsed', 'collapsing', 'expanded', and 'expanding' states.
+ * The function called when the side panel is transitioning between states.
+ * Use this to track when the side panel is animating between 'collapsed', 'collapsing', 'expanded', and 'expanding' states.
* This can be particularly helpful if child components need to react specifically to these states.
*
- * @param SidePanelInternalStates
+ * @param SidePanelTransitionStates
*/
- onStateChange?: (state?: SidePanelInternalStates) => void;
+ onStateTransition?: (state?: SidePanelTransitionStates) => void;
/**
- * Style variants of the side panel
+ * The style variant of the side panel
*
* @default 'standard'
*/
@@ -105,33 +99,30 @@ const containerVariantStyle: {[K in SidePanelVariant]: CSSObject} = {
},
};
+export const SidePanelContext = React.createContext({
+ state: 'expanded',
+ origin: 'left',
+});
+
const SidePanel = ({
'aria-label': ariaLabel = 'navigation pane',
children,
- collapsed: collapsedProp,
collapsedWidth = 64,
- defaultCollapsed = false,
+ expanded = true,
+ expandedWidth = 320,
height = '100%',
- id,
onAnimationEnd,
onAnimationStart,
- onCollapsedChange,
- onStateChange,
+ onExpandedChange,
+ onStateTransition,
origin = 'left',
variant = 'standard',
- width = 320,
...elemProps
}: SidePanelProps) => {
const mounted = React.useRef(false);
- const [collapsed, setCollapsed, isControlled] = useControllableState(
- collapsedProp,
- defaultCollapsed
+ const [state, setState] = React.useState(
+ expanded ? 'expanded' : 'collapsed'
);
- const [internalState, setInternalState] = React.useState(
- collapsed ? 'collapsed' : 'expanded'
- );
- // Optimization would be to only call uuid in a hook if this is an uncontrolled component
- const sidePanelId = useUniqueId(id);
// This is meant to prevent animations when the component renders for the first time.
// mounted.current will only be false on the first pass
@@ -140,24 +131,20 @@ const SidePanel = ({
}, []);
React.useEffect(() => {
- if (typeof onCollapsedChange !== 'undefined') {
- onCollapsedChange(collapsed);
+ if (typeof onExpandedChange !== 'undefined') {
+ onExpandedChange(expanded);
}
- }, [collapsed, onCollapsedChange]);
+ }, [expanded, onExpandedChange]);
React.useEffect(() => {
- if (typeof onStateChange !== 'undefined') {
- onStateChange(internalState);
+ if (typeof onStateTransition !== 'undefined') {
+ onStateTransition(state);
}
- }, [internalState, onStateChange]);
+ }, [state, onStateTransition]);
const motion = {
- collapse: createKeyframes(collapsedWidth, width),
- expand: createKeyframes(width, collapsedWidth),
- };
-
- const handleClick = () => {
- setCollapsed(!collapsed);
+ collapse: createKeyframes(expandedWidth, collapsedWidth),
+ expand: createKeyframes(collapsedWidth, expandedWidth),
};
const handleAnimationEnd = (event: React.AnimationEvent) => {
@@ -166,7 +153,7 @@ const SidePanel = ({
event.animationName === motion.collapse.name ||
event.animationName === motion.expand.name
) {
- setInternalState(collapsed ? 'collapsed' : 'expanded');
+ setState(expanded ? 'expanded' : 'collapsed');
}
}
@@ -181,7 +168,7 @@ const SidePanel = ({
event.animationName === motion.collapse.name ||
event.animationName === motion.expand.name
) {
- setInternalState(collapsed ? 'collapsing' : 'expanding');
+ setState(expanded ? 'expanding' : 'collapsing');
}
}
@@ -190,38 +177,25 @@ const SidePanel = ({
}
};
- // Note: Depending on the collapsed width, the button could "jump" to it's final position.
- const buttonStyle = css({
- position: 'absolute',
- top: spacing.l,
- right: internalState === 'collapsed' ? 0 : origin === 'left' ? spacing.s : undefined,
- left: internalState === 'collapsed' ? 0 : origin === 'right' ? spacing.s : undefined,
- margin: internalState === 'collapsed' ? 'auto' : undefined,
- transform: collapsed
- ? `scaleX(${origin === 'left' ? '1' : '-1'})`
- : `scaleX(${origin === 'left' ? '-1' : '1'})`,
- });
-
return (
);
};
+/**
+ * A toggle button styled specifically for the side panel container.
+ */
+const ToggleButton = ({
+ variant = IconButton.Variant.CircleFilled,
+ icon = transformationImportIcon,
+ ...rest
+}: IconButtonProps) => {
+ const context = React.useContext(SidePanelContext);
+
+ // Note: Depending on the collapsed width, the button could "jump" to it's final position.
+ const buttonStyle = css({
+ position: 'absolute',
+ top: spacing.l,
+ right: context.state === 'collapsed' ? 0 : context.origin === 'left' ? spacing.s : undefined,
+ left: context.state === 'collapsed' ? 0 : context.origin === 'right' ? spacing.s : undefined,
+ margin: context.state === 'collapsed' ? 'auto' : undefined,
+ transform:
+ context.state === 'collapsed' || context.state === 'collapsing'
+ ? `scaleX(${context.origin === 'left' ? '1' : '-1'})`
+ : `scaleX(${context.origin === 'left' ? '-1' : '1'})`,
+ });
+
+ return ;
+};
+
+const HiddenLabel = ({children, ...elemProps}: React.HTMLAttributes) => (
+
+ {children}
+
+);
+
+SidePanel.HiddenLabel = HiddenLabel;
+SidePanel.ToggleButton = ToggleButton;
export default SidePanel;
diff --git a/modules/_labs/side-panel/react/lib/hooks.ts b/modules/_labs/side-panel/react/lib/hooks.ts
index bbab3eec7a..07a2168e55 100644
--- a/modules/_labs/side-panel/react/lib/hooks.ts
+++ b/modules/_labs/side-panel/react/lib/hooks.ts
@@ -1,23 +1,48 @@
import * as React from 'react';
-// TODO: From our friend NicholasBoll with slight modifications: https://codesandbox.io/s/controllable-tooltip-sfnus?file=/src/useControllableState.ts
-export const useControllableState = (
- value: T,
- defaultValue: T | (() => T)
-): [T, (value: T) => void, boolean] => {
- const [valueState, setValueState] = React.useState(defaultValue);
- const isControlled = value !== undefined;
-
- // TODO: warn about switching between controlled and uncontrolled
- // TODO: warn about changing the default value
-
- const effectiveValue = isControlled ? value : valueState;
-
- const updateValue = (newValue: T) => {
- if (!isControlled) {
- setValueState(newValue);
- }
+import {useUniqueId} from '@workday/canvas-kit-react-common';
+import {SidePanelProps} from './SidePanel';
+
+export interface UseSidePanelProps extends Pick, 'id'> {
+ initialExpanded?: boolean;
+ sidePanelId?: string;
+ labelId?: string;
+}
+
+export const useSidePanel = ({
+ initialExpanded = true,
+ sidePanelId: sidePanelIdParam,
+ labelId: labelIdParam,
+}: UseSidePanelProps) => {
+ const [expanded, setExpanded] = React.useState(initialExpanded);
+ const sidePanelId = useUniqueId(sidePanelIdParam);
+ const labelId = useUniqueId(labelIdParam);
+
+ const handleClick = () => {
+ setExpanded(!expanded);
+ };
+
+ const panelProps: Partial = {
+ expanded: expanded,
+ id: sidePanelId,
+ 'aria-labelledby': typeof labelId === 'undefined' ? undefined : labelId,
};
- return [effectiveValue, updateValue, isControlled];
+ const labelProps = {
+ id: labelId,
+ };
+
+ const buttonProps = {
+ 'aria-controls': sidePanelId,
+ 'aria-expanded': expanded,
+ onClick: handleClick,
+ };
+
+ return {
+ expanded,
+ setExpanded,
+ panelProps,
+ labelProps,
+ buttonProps,
+ };
};
diff --git a/modules/_labs/side-panel/react/stories/stories.tsx b/modules/_labs/side-panel/react/stories/stories.tsx
index d50ac8c0d3..59b08a0dc1 100644
--- a/modules/_labs/side-panel/react/stories/stories.tsx
+++ b/modules/_labs/side-panel/react/stories/stories.tsx
@@ -1,12 +1,19 @@
///
+/** @jsx jsx */
import * as React from 'react';
import withReadme from 'storybook-readme/with-readme';
-
-import SidePanel from '../index';
+import {css, jsx} from '@emotion/core';
+import SidePanel, {useSidePanel} from '../index';
import {Button} from '@workday/canvas-kit-react-button';
import {colors, depth} from '@workday/canvas-kit-react-core';
+import {type} from '@workday/canvas-kit-labs-react-core';
+import {AccentIcon} from '@workday/canvas-kit-react-icon';
+import {rocketIcon} from '@workday/canvas-accent-icons-web';
+import {plusIcon} from '@workday/canvas-system-icons-web';
import README from '../README.md';
+import {accessibleHide} from '@workday/canvas-kit-react-common';
+import {SidePanelTransitionStates} from '../lib/SidePanel';
export default {
title: 'Labs|Side Panel/React',
@@ -15,23 +22,67 @@ export default {
};
export const Default = () => {
- const heightOffset = 40;
+ const height = `calc(100vh - 80px)`;
+ const {expanded, panelProps, labelProps, buttonProps} = useSidePanel({});
+ const [panelState, setPanelState] = React.useState(
+ expanded ? 'expanded' : 'collapsed'
+ );
+
+ const headerStyles = state =>
+ css(state === 'expanded' ? {} : accessibleHide, {
+ display: 'flex',
+ alignItems: 'center',
+ padding: `16px 12px`,
+ });
+ /**
+ * This is a typical use case for a side panel. The Side Panel has a toggle button and
+ * a header. Together, these three elements make a expandable panel accessible and the
+ * useSidePanel hook helps connect the three elements together through prop spreading.
+ */
return (
- {
- console.log(`collapsed state is: ${collapsed ? 'true' : 'false'}`);
- }}
- onStateChange={internalState => {
- console.log(`Side Panel is ${internalState}`);
- }}
- >
+
+
+
+
+ Quick Tasks
+
+
+
+
+ );
+};
+
+export const NoHeaderPermanentlyOpen = () => {
+ const height = `calc(100vh - 80px)`;
+ const {panelProps, labelProps} = useSidePanel({});
+
+ /**
+ * We're not using buttonProps in this example because while there's a button,
+ * it's not controlling the Side Panel. It's meant for some other function.
+ *
+ * Because we don't have an explicit header, we're using a visually hidden element to
+ * label the side panel. This is done by spreading panelProps and labelProps to their
+ * respective elements (connects the two via aria-labelledby).
+ */
+ return (
+
+ Quick Tasks
+
)}
From 988348de5f0926be77c018bcf0c2b96e14e7ddca Mon Sep 17 00:00:00 2001
From: lychyi <14299381+lychyi@users.noreply.github.com>
Date: Thu, 8 Oct 2020 09:01:03 -0700
Subject: [PATCH 20/49] fix(side-panel): Add type=button
---
modules/_labs/side-panel/react/lib/SidePanel.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/_labs/side-panel/react/lib/SidePanel.tsx b/modules/_labs/side-panel/react/lib/SidePanel.tsx
index 4566e12d7f..9a6395898a 100644
--- a/modules/_labs/side-panel/react/lib/SidePanel.tsx
+++ b/modules/_labs/side-panel/react/lib/SidePanel.tsx
@@ -239,7 +239,7 @@ const ToggleButton = ({
: `scaleX(${context.origin === 'left' ? '-1' : '1'})`,
});
- return ;
+ return ;
};
const HiddenLabel = ({children, ...elemProps}: React.HTMLAttributes) => (
From 7f6052e95e13abb12fbfe3bc9e4d4f177eb35f56 Mon Sep 17 00:00:00 2001
From: lychyi <14299381+lychyi@users.noreply.github.com>
Date: Thu, 8 Oct 2020 10:23:45 -0700
Subject: [PATCH 21/49] chore(side-panel): Add deps for stories
---
modules/_labs/side-panel/react/package.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/modules/_labs/side-panel/react/package.json b/modules/_labs/side-panel/react/package.json
index 86cde72472..f3990ae7ce 100644
--- a/modules/_labs/side-panel/react/package.json
+++ b/modules/_labs/side-panel/react/package.json
@@ -51,6 +51,8 @@
"@workday/canvas-kit-react-button": "^4.2.0",
"@workday/canvas-kit-react-common": "^4.2.0",
"@workday/canvas-kit-react-core": "^4.2.0",
+ "@workday/canvas-kit-react-icon": "^4.2.0",
+ "@workday/canvas-accent-icons-web": "^1.0.21",
"@workday/canvas-system-icons-web": "^1.0.20"
},
"devDependencies": {
From 9525c734ccf63178b150e084932b8beb4c48b258 Mon Sep 17 00:00:00 2001
From: lychyi <14299381+lychyi@users.noreply.github.com>
Date: Mon, 12 Oct 2020 20:45:47 -0700
Subject: [PATCH 22/49] feat(side-panel): Add 'as' prop to specify the rendered
element
Also refactored hooks and the stories to match.
---
.../_labs/side-panel/react/lib/SidePanel.tsx | 35 +++++----
modules/_labs/side-panel/react/lib/hooks.ts | 17 ++---
modules/_labs/side-panel/react/package.json | 1 +
.../side-panel/react/stories/stories.tsx | 71 +++++++------------
4 files changed, 58 insertions(+), 66 deletions(-)
diff --git a/modules/_labs/side-panel/react/lib/SidePanel.tsx b/modules/_labs/side-panel/react/lib/SidePanel.tsx
index 9a6395898a..182b00b30c 100644
--- a/modules/_labs/side-panel/react/lib/SidePanel.tsx
+++ b/modules/_labs/side-panel/react/lib/SidePanel.tsx
@@ -1,5 +1,6 @@
/** @jsx jsx */
import * as React from 'react';
+import styled from '@emotion/styled';
import {css, jsx, keyframes, CSSObject} from '@emotion/core';
import {IconButton, IconButtonProps} from '@workday/canvas-kit-react-button';
import {spacing, colors, depth} from '@workday/canvas-kit-react-core';
@@ -11,10 +12,11 @@ export type SidePanelTransitionStates = 'collapsed' | 'collapsing' | 'expanded'
export interface SidePanelProps extends React.HTMLAttributes {
/**
- * The `aria-label` attribute for the side panel and button. This is required for accessibility.
+ * The element the side panel will render as (e.g. 'div').
*
+ * @default 'section'
*/
- 'aria-label'?: string;
+ as?: React.ElementType;
/**
* The width of the component (in `px` if it's a `number`) when it is collapsed.
*
@@ -44,10 +46,6 @@ export interface SidePanelProps extends React.HTMLAttributes {
* @default 'left'
*/
origin?: 'left' | 'right';
- /**
- * InternalState
- */
- state?: SidePanelTransitionStates;
/**
* The function called when the side panel's `expanded` state changes. States like 'collapsing' and 'expanding' are tracked in another callback: `onStateChange`
*
@@ -99,13 +97,19 @@ const containerVariantStyle: {[K in SidePanelVariant]: CSSObject} = {
},
};
+const Panel = styled('section')>({
+ overflow: 'hidden',
+ position: 'relative',
+ boxSizing: 'border-box',
+});
+
export const SidePanelContext = React.createContext({
state: 'expanded',
origin: 'left',
});
const SidePanel = ({
- 'aria-label': ariaLabel = 'navigation pane',
+ as = 'section',
children,
collapsedWidth = 64,
expanded = true,
@@ -178,14 +182,11 @@ const SidePanel = ({
};
return (
-
{children}
-
+
);
};
+// For SidePanel, we're leveraging a hidden span to label this toggle button via `aria-labelledby`.
+// This type removes the requirement for `aria-label` in this component
+export type ToggleButtonProps = Omit &
+ Partial>;
+
/**
* A toggle button styled specifically for the side panel container.
*/
@@ -223,7 +229,7 @@ const ToggleButton = ({
variant = IconButton.Variant.CircleFilled,
icon = transformationImportIcon,
...rest
-}: IconButtonProps) => {
+}: ToggleButtonProps) => {
const context = React.useContext(SidePanelContext);
// Note: Depending on the collapsed width, the button could "jump" to it's final position.
@@ -239,6 +245,7 @@ const ToggleButton = ({
: `scaleX(${context.origin === 'left' ? '-1' : '1'})`,
});
+ // @ts-ignore We don't need this to have aria-label, let the user decide to use that or aria-labelledby on the ToggleButton component
return ;
};
diff --git a/modules/_labs/side-panel/react/lib/hooks.ts b/modules/_labs/side-panel/react/lib/hooks.ts
index 07a2168e55..67c33c84cd 100644
--- a/modules/_labs/side-panel/react/lib/hooks.ts
+++ b/modules/_labs/side-panel/react/lib/hooks.ts
@@ -5,17 +5,17 @@ import {SidePanelProps} from './SidePanel';
export interface UseSidePanelProps extends Pick, 'id'> {
initialExpanded?: boolean;
- sidePanelId?: string;
+ panelId?: string;
labelId?: string;
}
export const useSidePanel = ({
initialExpanded = true,
- sidePanelId: sidePanelIdParam,
+ panelId: panelIdParam,
labelId: labelIdParam,
}: UseSidePanelProps) => {
const [expanded, setExpanded] = React.useState(initialExpanded);
- const sidePanelId = useUniqueId(sidePanelIdParam);
+ const panelId = useUniqueId(panelIdParam);
const labelId = useUniqueId(labelIdParam);
const handleClick = () => {
@@ -24,17 +24,18 @@ export const useSidePanel = ({
const panelProps: Partial = {
expanded: expanded,
- id: sidePanelId,
- 'aria-labelledby': typeof labelId === 'undefined' ? undefined : labelId,
+ id: panelId,
+ 'aria-labelledby': labelId,
};
const labelProps = {
id: labelId,
};
- const buttonProps = {
- 'aria-controls': sidePanelId,
+ const controlProps = {
+ 'aria-controls': panelId,
'aria-expanded': expanded,
+ 'aria-labelledby': labelId,
onClick: handleClick,
};
@@ -43,6 +44,6 @@ export const useSidePanel = ({
setExpanded,
panelProps,
labelProps,
- buttonProps,
+ controlProps,
};
};
diff --git a/modules/_labs/side-panel/react/package.json b/modules/_labs/side-panel/react/package.json
index f3990ae7ce..eb021ffe5b 100644
--- a/modules/_labs/side-panel/react/package.json
+++ b/modules/_labs/side-panel/react/package.json
@@ -48,6 +48,7 @@
],
"dependencies": {
"@emotion/core": "^10.0.28",
+ "@emotion/styled": "^10.0.27",
"@workday/canvas-kit-react-button": "^4.2.0",
"@workday/canvas-kit-react-common": "^4.2.0",
"@workday/canvas-kit-react-core": "^4.2.0",
diff --git a/modules/_labs/side-panel/react/stories/stories.tsx b/modules/_labs/side-panel/react/stories/stories.tsx
index 59b08a0dc1..bb0d8b948e 100644
--- a/modules/_labs/side-panel/react/stories/stories.tsx
+++ b/modules/_labs/side-panel/react/stories/stories.tsx
@@ -23,7 +23,7 @@ export default {
export const Default = () => {
const height = `calc(100vh - 80px)`;
- const {expanded, panelProps, labelProps, buttonProps} = useSidePanel({});
+ const {expanded, panelProps, labelProps, controlProps} = useSidePanel({});
const [panelState, setPanelState] = React.useState(
expanded ? 'expanded' : 'collapsed'
);
@@ -35,20 +35,15 @@ export const Default = () => {
padding: `16px 12px`,
});
- /**
- * This is a typical use case for a side panel. The Side Panel has a toggle button and
- * a header. Together, these three elements make a expandable panel accessible and the
- * useSidePanel hook helps connect the three elements together through prop spreading.
- */
return (
+
- Quick Tasks
+ Tasks Panel
-
);
};
@@ -57,17 +52,9 @@ export const NoHeaderPermanentlyOpen = () => {
const height = `calc(100vh - 80px)`;
const {panelProps, labelProps} = useSidePanel({});
- /**
- * We're not using buttonProps in this example because while there's a button,
- * it's not controlling the Side Panel. It's meant for some other function.
- *
- * Because we don't have an explicit header, we're using a visually hidden element to
- * label the side panel. This is done by spreading panelProps and labelProps to their
- * respective elements (connects the two via aria-labelledby).
- */
return (
- Quick Tasks
+ Tasks Panel
);
};
From e6c76d75d1bdb345b2c1b4e5107a61dde69da6ce Mon Sep 17 00:00:00 2001
From: lychyi <14299381+lychyi@users.noreply.github.com>
Date: Mon, 12 Oct 2020 21:43:09 -0700
Subject: [PATCH 23/49] docs(side-panel): Sync up docs
---
modules/_labs/side-panel/react/README.md | 110 +++++++++++++++++-
.../_labs/side-panel/react/lib/SidePanel.tsx | 8 +-
2 files changed, 109 insertions(+), 9 deletions(-)
diff --git a/modules/_labs/side-panel/react/README.md b/modules/_labs/side-panel/react/README.md
index 7db27cddbe..0ee193daad 100644
--- a/modules/_labs/side-panel/react/README.md
+++ b/modules/_labs/side-panel/react/README.md
@@ -15,15 +15,75 @@ yarn add @workday/canvas-kit-labs-react-side-panel
## Usage
```tsx
-import * as React from 'react';
-import SidePanel from '@workday/canvas-kit-labs-react-side-panel';
+import SidePanel, {useSidePanel} from '@workday/canvas-kit-labs-react-side-panel';
+
+/**
+ * A SidePanel is made up of three components
+ * - The panel itself
+ * - An accessible name (either an existing element or you can use )
+ * - A control that expands/collapses the SidePanel, usually as a child of
+ * A convenience hook is available for setting the state of the SidePanel along with the proper aria- attributes
+ *
+ */
+const [panelProps, labelProps, controlProps] = useSidePanel({});
+
+
+
+
Tasks Panel
+ {/* Your panel contents */}
+
-;
+```
+## Hooks
+### `useSidePanel`
+This hook manages the state and `aria-` attributes for the SidePanel. It takes in a `config` object:
+* `intialExpanded: boolean`: Specifies the initial expanded/collapsed state of the side panel.
+* `panelId: string`: specifies the id of the panel, if `undefined` then this hook will generate one
+* `labelId: string` specifies the id of the label, if `undefined` then this hook will generate one
+
+and returns:
+* `expanded: boolean`: The state for the `SidePanel`'s `expanded` prop.
+* `setExpanded: (expanded?: boolean) => void`: A function that sets the `expanded` state.
+* `panelProps: object`: Contains the `expanded`, `id`, and `aria-labelledby` props for the `SidePanel` (or equivalent) component.
+* `labelProps: object`: Contains the `id` prop for the label.
+* `controlProps: object`: Contains the `aria-controls`, `aria-expanded`, `aria-labelledby`, and click handler for the side panel's control element.
+
+#### Usage
+```tsx
+const [expanded, setExpanded, panelProps, labelProps, controlProps] = useSidePanel({
+ initialExpanded: true,
+ panelId: 'side-panel-1',
+ labelId: 'side-panel-label-1'
+});
+
+
+ {/* Make sure the control is first focusable */}
+
+ Example Panel
+ {/* Rest of the children here */}
+
+
+or
+
+
+ {/* If you want to use your own button */}
+
+ {/* If you already have an element that can be an accessible name for the panel */}
+
Example Panel
+
```
## Static Properties
-> None
+#### `HiddenLabel`
+> Because a side panel is `role='region'` it must have an accessible name. You can use `` in conjunction with the `useSidePanel` hook or manually using `aria-labelledby`. This specifies the panel's accessible name if you don't have one as an element already (if you do, use that instead).
+
+> `HiddenLabel` hides the element visually and from screen readers. It is solely used to provide an accessible name.
+
+#### `ToggleButton`
+> `` is a control that is meant to toggle between `expanded = true` and `expanded = false` states. It must be used within the `SidePanel` component as a child. Use in conjunction with `useSidePanel`'s `controlProps`, otherwise it does not come with explicit `onClick` handlers.
+
+> **Important**: For accessibility purposes, it must be the first focusable element. We recommend that you keep it as the first child of `SidePanel`.
## Component Props
@@ -33,4 +93,44 @@ import SidePanel from '@workday/canvas-kit-labs-react-side-panel';
### Optional
-> None
+#### `as: React.ElementType`
+> The element the side panel will render as.
+
+Default: `'section'`
+
+#### `collapsedWidth: number | string`
+> The width of the component (in `px` if it's a `number`) when it is collapsed.
+
+Default: `64`
+
+#### `expanded: boolean`
+> If true, sets the expanded state of the side panel
+
+Default: `false`
+
+#### `expandedWidth: number | string`
+> The width of the component (in `px` if it's a `number`) when it is expanded.
+
+Default: `320`
+
+#### `height: number | string`
+> The height of the component (in `px` if it's a `number`).
+
+Default: `'100%'`
+
+#### `origin: 'left' | 'right'`
+> Which side the side panel is meant to originate from.
+
+Default: `'left'`
+
+#### `onExpandedChange: (expanded?: boolean) => void`
+> The function called when the side panel's `expanded` state changes. States like `'collapsing'` and `'expanding'` are tracked in another callback: `onStateChange`
+
+#### `onStateTransition: (state?: SidePanelTransitionStates) => void`
+> The function called when the side panel is transitioning between states. Use this to track when the side panel is animating between 'collapsed', 'collapsing', 'expanded', and 'expanding' states. This can be particularly helpful if child components need to react specifically to these states.
+
+#### `variant: SidePanelVariant`
+> The style variant of the side panel. 'standard' is with a `soap100` background, no depth. 'alternate' is a `frenchVanilla100` background with a level 3 depth.
+
+Default: `'standard'`
+
diff --git a/modules/_labs/side-panel/react/lib/SidePanel.tsx b/modules/_labs/side-panel/react/lib/SidePanel.tsx
index 182b00b30c..f51c8339de 100644
--- a/modules/_labs/side-panel/react/lib/SidePanel.tsx
+++ b/modules/_labs/side-panel/react/lib/SidePanel.tsx
@@ -47,21 +47,21 @@ export interface SidePanelProps extends React.HTMLAttributes {
*/
origin?: 'left' | 'right';
/**
- * The function called when the side panel's `expanded` state changes. States like 'collapsing' and 'expanding' are tracked in another callback: `onStateChange`
+ * The function called when the side panel's `expanded` state changes. States like `'collapsing'` and `'expanding'` are tracked in another callback: `onStateChange`
*
* @param boolean
*/
onExpandedChange?: (expanded?: boolean) => void;
/**
* The function called when the side panel is transitioning between states.
- * Use this to track when the side panel is animating between 'collapsed', 'collapsing', 'expanded', and 'expanding' states.
+ * Use this to track when the side panel is animating between `'collapsed'`, `'collapsing'`, `'expanded'`, and `'expanding'` states.
* This can be particularly helpful if child components need to react specifically to these states.
*
* @param SidePanelTransitionStates
*/
onStateTransition?: (state?: SidePanelTransitionStates) => void;
/**
- * The style variant of the side panel
+ * The style variant of the side panel. 'standard' is with a `soap100` background, no depth. 'alternate' is a `frenchVanilla100` background with a level 3 depth.
*
* @default 'standard'
*/
@@ -250,7 +250,7 @@ const ToggleButton = ({
};
const HiddenLabel = ({children, ...elemProps}: React.HTMLAttributes) => (
-
+
{children}
);
From ee30da88ea7e215c419ba9c1dbf66db4d15d327c Mon Sep 17 00:00:00 2001
From: lychyi <14299381+lychyi@users.noreply.github.com>
Date: Tue, 13 Oct 2020 08:13:40 -0700
Subject: [PATCH 24/49] fix(side-panel): Remove hidden label static
Replace with more standard way of doing it, adjust docs
---
modules/_labs/side-panel/react/README.md | 9 ++---
.../_labs/side-panel/react/lib/SidePanel.tsx | 9 -----
modules/_labs/side-panel/react/package.json | 1 -
.../side-panel/react/stories/stories.tsx | 36 +++++++++----------
4 files changed, 20 insertions(+), 35 deletions(-)
diff --git a/modules/_labs/side-panel/react/README.md b/modules/_labs/side-panel/react/README.md
index 0ee193daad..83e5d13c07 100644
--- a/modules/_labs/side-panel/react/README.md
+++ b/modules/_labs/side-panel/react/README.md
@@ -20,7 +20,7 @@ import SidePanel, {useSidePanel} from '@workday/canvas-kit-labs-react-side-panel
/**
* A SidePanel is made up of three components
* - The panel itself
- * - An accessible name (either an existing element or you can use )
+ * - An accessible name (either an existing element or you can use )
* - A control that expands/collapses the SidePanel, usually as a child of
* A convenience hook is available for setting the state of the SidePanel along with the proper aria- attributes
*
@@ -59,7 +59,7 @@ const [expanded, setExpanded, panelProps, labelProps, controlProps] = useSidePan
{/* Make sure the control is first focusable */}
- Example Panel
+ Example Panel
{/* Rest of the children here */}
@@ -75,11 +75,6 @@ or
## Static Properties
-#### `HiddenLabel`
-> Because a side panel is `role='region'` it must have an accessible name. You can use `` in conjunction with the `useSidePanel` hook or manually using `aria-labelledby`. This specifies the panel's accessible name if you don't have one as an element already (if you do, use that instead).
-
-> `HiddenLabel` hides the element visually and from screen readers. It is solely used to provide an accessible name.
-
#### `ToggleButton`
> `` is a control that is meant to toggle between `expanded = true` and `expanded = false` states. It must be used within the `SidePanel` component as a child. Use in conjunction with `useSidePanel`'s `controlProps`, otherwise it does not come with explicit `onClick` handlers.
diff --git a/modules/_labs/side-panel/react/lib/SidePanel.tsx b/modules/_labs/side-panel/react/lib/SidePanel.tsx
index f51c8339de..e1f505646c 100644
--- a/modules/_labs/side-panel/react/lib/SidePanel.tsx
+++ b/modules/_labs/side-panel/react/lib/SidePanel.tsx
@@ -4,7 +4,6 @@ import styled from '@emotion/styled';
import {css, jsx, keyframes, CSSObject} from '@emotion/core';
import {IconButton, IconButtonProps} from '@workday/canvas-kit-react-button';
import {spacing, colors, depth} from '@workday/canvas-kit-react-core';
-import {accessibleHide} from '@workday/canvas-kit-react-common';
import {transformationImportIcon} from '@workday/canvas-system-icons-web';
export type SidePanelVariant = 'standard' | 'alternate';
@@ -184,7 +183,6 @@ const SidePanel = ({
return (
;
};
-const HiddenLabel = ({children, ...elemProps}: React.HTMLAttributes) => (
-
- {children}
-
-);
-
-SidePanel.HiddenLabel = HiddenLabel;
SidePanel.ToggleButton = ToggleButton;
export default SidePanel;
diff --git a/modules/_labs/side-panel/react/package.json b/modules/_labs/side-panel/react/package.json
index eb021ffe5b..95f51dbe4b 100644
--- a/modules/_labs/side-panel/react/package.json
+++ b/modules/_labs/side-panel/react/package.json
@@ -50,7 +50,6 @@
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
"@workday/canvas-kit-react-button": "^4.2.0",
- "@workday/canvas-kit-react-common": "^4.2.0",
"@workday/canvas-kit-react-core": "^4.2.0",
"@workday/canvas-kit-react-icon": "^4.2.0",
"@workday/canvas-accent-icons-web": "^1.0.21",
diff --git a/modules/_labs/side-panel/react/stories/stories.tsx b/modules/_labs/side-panel/react/stories/stories.tsx
index bb0d8b948e..b5db6477ce 100644
--- a/modules/_labs/side-panel/react/stories/stories.tsx
+++ b/modules/_labs/side-panel/react/stories/stories.tsx
@@ -2,7 +2,7 @@
/** @jsx jsx */
import * as React from 'react';
import withReadme from 'storybook-readme/with-readme';
-import {css, jsx} from '@emotion/core';
+import {jsx} from '@emotion/core';
import SidePanel, {useSidePanel} from '../index';
import {Button} from '@workday/canvas-kit-react-button';
import {colors, depth} from '@workday/canvas-kit-react-core';
@@ -10,9 +10,7 @@ import {type} from '@workday/canvas-kit-labs-react-core';
import {AccentIcon} from '@workday/canvas-kit-react-icon';
import {rocketIcon} from '@workday/canvas-accent-icons-web';
import {plusIcon} from '@workday/canvas-system-icons-web';
-
import README from '../README.md';
-import {accessibleHide} from '@workday/canvas-kit-react-common';
import {SidePanelTransitionStates} from '../lib/SidePanel';
export default {
@@ -24,21 +22,17 @@ export default {
export const Default = () => {
const height = `calc(100vh - 80px)`;
const {expanded, panelProps, labelProps, controlProps} = useSidePanel({});
- const [panelState, setPanelState] = React.useState(
- expanded ? 'expanded' : 'collapsed'
- );
-
- const headerStyles = state =>
- css(state === 'expanded' ? {} : accessibleHide, {
- display: 'flex',
- alignItems: 'center',
- padding: `16px 12px`,
- });
return (
-
+
-