Feat/editor category pass2 (#290)

* feat(design-system): add editor skeletons

* use css media queries rather than JS media queries

* init remake

* feat: add basis of toggle atom

* finish toggle

* use pointer cursor with toggle

* sync

* feat: add server message in editor

* cleanup storybook

* add short editor item and data model for categories

* chore: fix build by moving jest version downward

* chore: remove old category editor

* chore: fix EditorCategoryShort index

* add editor wiring and styling updates

* fix linting issues
This commit is contained in:
41666 2021-07-05 12:18:40 -05:00 committed by GitHub
parent a37d481b18
commit 7d681d69d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 927 additions and 1100 deletions

View file

@ -0,0 +1,12 @@
import { mockCategory } from '@roleypoly/design-system/fixtures/storyData';
import { render } from '@testing-library/react';
import { EditorCategoryShort } from './EditorCategoryShort';
it('triggers onOpen when clicked', async () => {
const onOpen = jest.fn();
const view = render(<EditorCategoryShort category={mockCategory} onOpen={onOpen} />);
view.getByRole('menuitem')?.click();
expect(onOpen).toHaveBeenCalled();
});

View file

@ -0,0 +1,30 @@
import { mockCategory } from '@roleypoly/design-system/fixtures/storyData';
import ReactTooltip from 'react-tooltip';
import styled from 'styled-components';
import { EditorCategoryShort } from './EditorCategoryShort';
const decorator = (story) => (
<Wrapper>
{story()}
<ReactTooltip />
</Wrapper>
);
export default {
title: 'Molecules/Short Category',
component: EditorCategoryShort,
args: {
category: mockCategory,
},
decorators: [decorator],
};
const Wrapper = styled.div`
box-shadow: 0 0 10px #000;
`;
export const shortEditor = (args) => <EditorCategoryShort {...args} />;
export const shortEditorHidden = (args) => <EditorCategoryShort {...args} />;
shortEditorHidden.args = {
category: { ...mockCategory, hidden: true },
};

View file

@ -0,0 +1,60 @@
import { transitions } from '@roleypoly/design-system/atoms/timings';
import styled from 'styled-components';
export const GrabberBox = styled.div`
display: flex;
align-items: center;
justify-content: center;
margin-right: 1em;
flex: 0;
cursor: grab;
position: relative;
::before {
content: '';
position: absolute;
top: -5px;
left: -8px;
width: 32px;
height: 32px;
background-color: rgba(0, 0, 0, 0.25);
opacity: 0;
border-radius: 50%;
transition: opacity ${transitions.actionable}s ease-in-out;
}
&:hover {
::before {
opacity: 1;
}
}
`;
export const Opener = styled.div`
opacity: 0;
transition: opacity ${transitions.actionable}s ease-in-out;
`;
export const Container = styled.div`
display: flex;
padding: 1em;
cursor: pointer;
&:hover {
${Opener} {
opacity: 1;
}
}
`;
export const Name = styled.div`
position: relative;
top: -2px;
margin-right: 1em;
`;
export const Flags = styled.div``;
export const Void = styled.div`
flex: 1;
`;

View file

@ -0,0 +1,31 @@
import { Category } from '@roleypoly/types';
import { DragEventHandler, MouseEventHandler } from 'react';
import { GoEyeClosed, GoGrabber, GoKebabHorizontal } from 'react-icons/go';
import {
Container,
Flags,
GrabberBox,
Name,
Opener,
Void,
} from './EditorCategoryShort.styled';
type ShortProps = {
category: Category;
onDrag?: DragEventHandler;
onOpen?: MouseEventHandler;
};
export const EditorCategoryShort = (props: ShortProps) => (
<Container onClick={props.onOpen} role="menuitem">
<GrabberBox onDrag={props.onDrag} role="button">
<GoGrabber />
</GrabberBox>
<Name>{props.category.name}</Name>
<Flags>{props.category.hidden && <GoEyeClosed data-tip="Hidden to Members" />}</Flags>
<Void />
<Opener role="button">
<GoKebabHorizontal />
</Opener>
</Container>
);

View file

@ -0,0 +1 @@
export * from './EditorCategoryShort';

View file

@ -3,4 +3,11 @@ import styled from 'styled-components';
export const RoleContainer = styled.div`
display: flex;
margin: 10px;
flex-wrap: wrap;
& > div {
/* This should be a Role element */
border: 1px solid rgba(0, 0, 0, 0.15);
margin: 1px;
}
`;

View file

@ -1,9 +1,9 @@
import { FaderOpacity } from '@roleypoly/design-system/atoms/fader';
import { HorizontalSwitch } from '@roleypoly/design-system/atoms/horizontal-switch';
import { Popover } from '@roleypoly/design-system/atoms/popover';
import { Role } from '@roleypoly/design-system/atoms/role';
import { Space } from '@roleypoly/design-system/atoms/space';
import { TextInput, TextInputWithIcon } from '@roleypoly/design-system/atoms/text-input';
import { Toggle } from '@roleypoly/design-system/atoms/toggle';
import { Text } from '@roleypoly/design-system/atoms/typography';
import { RoleSearch } from '@roleypoly/design-system/molecules/role-search';
import { Category, CategoryType, Role as RoleType } from '@roleypoly/types';
@ -74,24 +74,22 @@ export const EditorCategory = (props: Props) => {
<Space />
<Text>Selection Type</Text>
<div>
<HorizontalSwitch
items={['Multiple', 'Single']}
value={typeEnumToSwitch(props.category.type)}
onChange={onUpdate('type', switchToTypeEnum)}
/>
<Toggle
state={props.category.type === CategoryType.Multi}
onChange={onUpdate('type', (x) =>
x ? CategoryType.Multi : CategoryType.Single
)}
>
Allow users to pick multiple roles
</Toggle>
</div>
<Space />
<Text>Visiblity</Text>
<div>
<HorizontalSwitch
items={['Visible', 'Hidden']}
value={props.category.hidden ? 'Hidden' : 'Visible'}
onChange={onUpdate('hidden', (a) => a === 'Hidden')}
/>
<Toggle state={props.category.hidden} onChange={onUpdate('hidden')}>
Hide category from users
</Toggle>
</div>
<Space />

View file

@ -0,0 +1,43 @@
import { Avatar, utils as avatarUtils } from '@roleypoly/design-system/atoms/avatar';
import { Text } from '@roleypoly/design-system/atoms/typography';
import { PresentableGuild } from '@roleypoly/types';
import styled, { css } from 'styled-components';
import { onSmallScreen } from '../../atoms/breakpoints';
type EditorMastheadProps = {
guild: PresentableGuild;
onSubmit: () => void;
onReset: () => void;
showSaveReset: boolean;
};
const MastheadContainer = styled.div`
display: flex;
flex: 1;
align-items: center;
justify-content: start;
padding-bottom: 0.5em;
${Text} {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 0.5em;
}
${onSmallScreen(css`
display: none;
`)}
`;
export const EditorMasthead = (props: EditorMastheadProps) => (
<MastheadContainer>
<Avatar
size={34}
hash={props.guild.guild.icon}
src={avatarUtils.avatarHash(props.guild.id, props.guild.guild.icon, 'icons')}
>
{avatarUtils.initialsFromName(props.guild.guild.name)}
</Avatar>
<Text>Server Editor</Text>
</MastheadContainer>
);

View file

@ -0,0 +1 @@
export * from './EditorMasthead';

View file

@ -1,9 +1,10 @@
import * as React from 'react';
import { ResetSubmit } from './ResetSubmit';
import { InlineResetSubmit, ResetSubmit } from './ResetSubmit';
export default {
title: 'Molecules',
title: 'Molecules/Reset and Submit',
component: ResetSubmit,
};
export const ResetAndSubmit = (args) => <ResetSubmit {...args} />;
export const normal = (args) => <ResetSubmit {...args} />;
export const inline = (args) => <InlineResetSubmit {...args} />;

View file

@ -1,19 +0,0 @@
import { onSmallScreen } from '@roleypoly/design-system/atoms/breakpoints';
import styled from 'styled-components';
export const Buttons = styled.div`
display: flex;
flex-wrap: wrap;
`;
export const Left = styled.div`
flex: 0;
${onSmallScreen`
flex: 1 1 100%;
order: 2;
`}
`;
export const Right = styled.div`
flex: 1;
`;

View file

@ -2,7 +2,7 @@ import { onSmallScreen } from '@roleypoly/design-system/atoms/breakpoints';
import { Button } from '@roleypoly/design-system/atoms/button';
import * as React from 'react';
import { MdRestore } from 'react-icons/md';
import styled from 'styled-components';
import styled, { css } from 'styled-components';
type Props = {
onSubmit: () => void;
@ -22,8 +22,14 @@ const Left = styled.div`
`}
`;
const Right = styled.div`
const Right = styled.div<{ inline?: boolean }>`
flex: 1;
${(props) =>
props.inline &&
css`
padding-left: 0.2em;
`}
`;
export const ResetSubmit = (props: Props) => {
@ -40,3 +46,20 @@ export const ResetSubmit = (props: Props) => {
</Buttons>
);
};
export const InlineResetSubmit = (props: Props) => {
return (
<Buttons>
<Left>
<Button color="muted" size="small" icon={<MdRestore />} onClick={props.onReset}>
Reset
</Button>
</Left>
<Right inline>
<Button onClick={props.onSubmit} size="small">
Submit
</Button>
</Right>
</Buttons>
);
};