Skip to content

Commit

Permalink
feat(dashboardeditor): add breakpoint switcher
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephen Stone committed Oct 9, 2020
1 parent 9c012e3 commit 5fdb1ad
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 66 deletions.
132 changes: 88 additions & 44 deletions src/components/DashboardEditor/DashboardEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ const propTypes = {
}),
/** supported card types */
supportedCardTypes: PropTypes.arrayOf(PropTypes.string),
/** if enabled, renders a ContentSwitcher with IconSwitches that allow for manually changing the breakpoint,
* regardless of the screen width
*/
breakpointSwitcher: PropTypes.shape({
enabled: PropTypes.bool,
initialValue: PropTypes.string,
}),
/** if provided, renders header content above preview */
renderHeader: PropTypes.func,
/** if provided, is used to render cards in dashboard */
Expand Down Expand Up @@ -62,6 +69,7 @@ const defaultProps = {
layouts: {},
},
supportedCardTypes: [CARD_TYPES.BAR, CARD_TYPES.TIMESERIES, CARD_TYPES.VALUE, CARD_TYPES.TABLE],
breakpointSwitcher: null,
renderHeader: null,
renderCardPreview: () => null,
headerBreadcrumbs: null,
Expand All @@ -87,17 +95,18 @@ const defaultProps = {
},
};

const BREAKPOINTS = {
TABLET: 1,
LAPTOP: 2,
SCREEN: 3,
FIT_TO_SCREEN: 0,
const LAYOUTS = {
FIT_TO_SCREEN: { breakpoint: 'max', index: 0 },
TABLET: { breakpoint: 'md', index: 1 },
LAPTOP: { breakpoint: 'lg', index: 2 },
SCREEN: { breakpoint: 'xk', index: 3 },
};

const DashboardEditor = ({
title,
initialValue,
supportedCardTypes,
breakpointSwitcher,
renderHeader,
renderCardPreview,
headerBreadcrumbs,
Expand All @@ -117,13 +126,22 @@ const DashboardEditor = ({
// show the gallery if no card is being edited
const [dashboardJson, setDashboardJson] = useState(initialValue);
const [selectedCardId, setSelectedCardId] = useState();
const [selectedBreakpoint, setSelectedBreakpoint] = useState(BREAKPOINTS.FIT_TO_SCREEN);
const [selectedBreakpointIndex, setSelectedBreakpointIndex] = useState(
breakpointSwitcher?.initialValue
? LAYOUTS[breakpointSwitcher.initialValue].index
: LAYOUTS.FIT_TO_SCREEN.index
);
const [currentBreakpoint, setCurrentBreakpoint] = useState(
breakpointSwitcher?.initialValue
? LAYOUTS[breakpointSwitcher.initialValue].breakpoint
: LAYOUTS.FIT_TO_SCREEN.breakpoint
);

useEffect(
() => {
window.dispatchEvent(new Event('resize'));
},
[selectedBreakpoint]
[selectedBreakpointIndex]
);

const addCard = type => {
Expand Down Expand Up @@ -167,46 +185,72 @@ const DashboardEditor = ({
onSubmit={onSubmit}
i18n={mergedI18N}
dashboardJson={dashboardJson}
selectedBreakpoint={selectedBreakpoint}
setSelectedBreakpoint={setSelectedBreakpoint}
selectedBreakpointIndex={selectedBreakpointIndex}
setSelectedBreakpointIndex={setSelectedBreakpointIndex}
breakpointSwitcher={breakpointSwitcher}
/>
)}
{notification}
<div className={`${baseClassName}--preview`}>
<DashboardGrid
isEditable
// onBreakpointChange={() => {}}
onLayoutChange={(newLayout, newLayouts) =>
setDashboardJson({
...dashboardJson,
layouts: newLayouts,
})
}
className={classNames({
[`${baseClassName}--preview-tablet`]: selectedBreakpoint === BREAKPOINTS.TABLET,
[`${baseClassName}--preview-laptop`]: selectedBreakpoint === BREAKPOINTS.LAPTOP,
[`${baseClassName}--preview-screen`]: selectedBreakpoint === BREAKPOINTS.SCREEN,
})}
>
{dashboardJson.cards.map(cardData => {
const isSelected = selectedCardId === cardData.id;
const onSelectCard = id => setSelectedCardId(id);
const onDuplicateCard = id => duplicateCard(id);
const onRemoveCard = id => removeCard(id);

// if function not defined, or it returns falsy, render default preview
return (
renderCardPreview(
cardData,
isSelected,
onSelectCard,
onDuplicateCard,
onRemoveCard
) ??
getCardPreview(cardData, isSelected, onSelectCard, onDuplicateCard, onRemoveCard)
);
})}
</DashboardGrid>
<div
className={classNames(`${baseClassName}--preview`, {
[`${baseClassName}--preview__breakpoint-switcher`]: breakpointSwitcher?.enabled,
})}
>
{breakpointSwitcher?.enabled && (
<div
className={classNames({
[`${baseClassName}--preview__tablet ${baseClassName}--preview__outline`]:
selectedBreakpointIndex === LAYOUTS.TABLET.index,
[`${baseClassName}--preview__laptop ${baseClassName}--preview__outline`]:
selectedBreakpointIndex === LAYOUTS.LAPTOP.index,
[`${baseClassName}--preview__screen ${baseClassName}--preview__outline`]:
selectedBreakpointIndex === LAYOUTS.SCREEN.index,
})}
>
<div className={`${baseClassName}--preview__breakpoint-info`}>
Edit dashboard layout at medium breakpoint
</div>
<DashboardGrid
isEditable
breakpoint={currentBreakpoint}
onBreakpointChange={newBreakpoint => {
setCurrentBreakpoint(newBreakpoint);
}}
onLayoutChange={(newLayout, newLayouts) =>
setDashboardJson({
...dashboardJson,
layouts: newLayouts,
})
}
>
{dashboardJson.cards.map(cardData => {
const isSelected = selectedCardId === cardData.id;
const onSelectCard = id => setSelectedCardId(id);
const onDuplicateCard = id => duplicateCard(id);
const onRemoveCard = id => removeCard(id);

// if function not defined, or it returns falsy, render default preview
return (
renderCardPreview(
cardData,
isSelected,
onSelectCard,
onDuplicateCard,
onRemoveCard
) ??
getCardPreview(
cardData,
isSelected,
onSelectCard,
onDuplicateCard,
onRemoveCard
)
);
})}
</DashboardGrid>
</div>
)}

{/* <pre style={{ paddingTop: '4rem' }}>{JSON.stringify(dashboardData, null, 4)}</pre> */}
</div>
</div>
Expand Down
26 changes: 26 additions & 0 deletions src/components/DashboardEditor/DashboardEditor.story.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,32 @@ storiesOf('Watson IoT Experimental/DashboardEditor', module)
/>
</div>
))
.add('with breakpointSwitcher', () => (
<div style={{ height: 'calc(100vh - 6rem)' }}>
<DashboardEditor
title={text('title', 'My dashboard')}
onAddImage={action('onAddImage')}
onEditTitle={action('onEditTitle')}
onImport={action('onImport')}
onExport={action('onExport')}
onDelete={action('onDelete')}
onCancel={action('onCancel')}
onSubmit={action('onSubmit')}
supportedCardTypes={array('supportedCardTypes', [
'BAR',
'TIMESERIES',
'VALUE',
'TABLE',
'OTHER',
])}
headerBreadcrumbs={[
<Link href="www.ibm.com">Dashboard library</Link>,
<Link href="www.ibm.com">Favorites</Link>,
]}
breakpointSwitcher={{ enabled: true }}
/>
</div>
))
.add('wrapped in SuiteHeader', () => (
<div style={{ height: 'calc(100vh - 3rem)', marginRight: '-3rem' }}>
<SuiteHeader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import {
Laptop16,
Screen16,
} from '@carbon/icons-react';
import { FileUploaderButton, TooltipIcon } from 'carbon-components-react';
import { FileUploaderButton, TooltipIcon, ContentSwitcher } from 'carbon-components-react';

import { settings } from '../../../constants/Settings';
import { Button, PageTitleBar, ContentSwitcher, IconSwitch } from '../../../index';
import { Button, PageTitleBar } from '../../../index';
import IconSwitch from '../../IconSwitch/IconSwitch';

const { iotPrefix } = settings;

Expand Down Expand Up @@ -51,9 +52,16 @@ const propTypes = {
/** The current dashboard's JSON */
dashboardJson: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
/** currently selected breakpoint which is being held in state by DashboardEditor */
selectedBreakpoint: PropTypes.string,
selectedBreakpointIndex: PropTypes.number,
/** handler to change the selectedBreakpoint state */
setSelectedBreakpoint: PropTypes.func,
setSelectedBreakpointIndex: PropTypes.func,
/** if enabled, renders a ContentSwitcher with IconSwitches that allow for manually changing the breakpoint,
* regardless of the screen width
*/
breakpointSwitcher: PropTypes.shape({
enabled: PropTypes.bool,
allowedBreakpoints: PropTypes.arrayOf(PropTypes.string),
}),
};

const defaultProps = {
Expand All @@ -73,8 +81,9 @@ const defaultProps = {
headerCancelButton: 'Cancel',
headerSubmitButton: 'Submit',
},
selectedBreakpoint: null,
setSelectedBreakpoint: null,
selectedBreakpointIndex: null,
setSelectedBreakpointIndex: null,
breakpointSwitcher: null,
};

const DashboardEditorHeader = ({
Expand All @@ -88,8 +97,9 @@ const DashboardEditorHeader = ({
onSubmit,
i18n,
dashboardJson,
selectedBreakpoint,
setSelectedBreakpoint,
selectedBreakpointIndex,
setSelectedBreakpointIndex,
breakpointSwitcher,
}) => {
const baseClassName = `${iotPrefix}--dashboard-editor-header`;
const extraContent = (
Expand All @@ -98,15 +108,19 @@ const DashboardEditorHeader = ({
{/* <span className="last-updated">Last updated: XYZ</span> */}
</div>
<div className={`${baseClassName}--bottom`}>
<ContentSwitcher
onChange={e => setSelectedBreakpoint(e.index)}
selectedIndex={selectedBreakpoint}
>
<IconSwitch name="fit-to-screen" text="Fit to screen" renderIcon={Maximize16} />
<IconSwitch name="tablet" text="Tablet view" renderIcon={Tablet16} />
<IconSwitch name="laptop" text="Laptop View" renderIcon={Laptop16} />
<IconSwitch name="screen" text="Desktop View" renderIcon={Screen16} />
</ContentSwitcher>
{breakpointSwitcher?.enabled && (
<ContentSwitcher
onChange={e => setSelectedBreakpointIndex(e.index)}
selectedIndex={selectedBreakpointIndex}
className={`${baseClassName}--bottom__switcher`}
>
<IconSwitch name="fit-to-screen" text="Fit to screen" renderIcon={Maximize16} />
<IconSwitch name="tablet" text="Tablet view" renderIcon={Tablet16} />
<IconSwitch name="laptop" text="Laptop View" renderIcon={Laptop16} />
<IconSwitch name="screen" text="Desktop View" renderIcon={Screen16} />
</ContentSwitcher>
)}

{// FileUploaderButton isn't a true button so extra styling is needed to make it look like a iconOnly button
onImport && (
<TooltipIcon
Expand Down
30 changes: 25 additions & 5 deletions src/components/DashboardEditor/_dashboard-editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
}
}

&__switcher {
margin-right: $spacing-05;
}

.#{$prefix}--btn--tertiary,
.#{$prefix}--btn--primary {
margin-left: $spacing-05;
Expand All @@ -50,8 +54,6 @@
}
&--right {
display: flex;
flex-direction: column;
align-items: flex-end;
}
}
&--preview {
Expand All @@ -66,15 +68,33 @@
margin: -$spacing-01;
}

&-tablet {
&__tablet {
width: 672px;
}
&-laptop {
&__laptop {
width: 1056px;
}
&-screen {
&__screen {
width: 1312px;
}

&__breakpoint-switcher {
background-color: $text-03;

.react-grid-layout {
background-color: $ui-background;
}
}

&__breakpoint-info {
padding: 1rem;
width: 100%;
background-color: $ui-03;
}

&__outline {
border: 1px dotted $ui-04;
}
}
&--sidebar {
height: 100%;
Expand Down

0 comments on commit 5fdb1ad

Please sign in to comment.