Refactor node packages to yarn workspaces & ditch next.js for CRA. (#161)

* chore: restructure project into yarn workspaces, remove next

* fix tests, remove webapp from terraform

* remove more ui deployment bits

* remove pages, fix FUNDING.yml

* remove isomorphism

* remove next providers

* fix linting issues

* feat: start basis of new web ui system on CRA

* chore: move types to @roleypoly/types package

* chore: move src/common/utils to @roleypoly/misc-utils

* chore: remove roleypoly/ path remappers

* chore: renmove vercel config

* chore: re-add worker-types to api package

* chore: fix type linting scope for api

* fix(web): craco should include all of packages dir

* fix(ci): change api webpack path for wrangler

* chore: remove GAR actions from CI

* chore: update codeql job

* chore: test better github dar matcher in lint-staged
This commit is contained in:
41666 2021-03-12 18:04:49 -05:00 committed by GitHub
parent 49e308507e
commit 2ff6588030
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
328 changed files with 16624 additions and 3525 deletions

View file

@ -0,0 +1,13 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { roleCategory } from '../../fixtures/storyData';
import { Role } from './Role';
it('fires an OnClick handler when clicked', () => {
const onClickMock = jest.fn();
const view = shallow(
<Role role={roleCategory[0]} selected={true} onClick={onClickMock} />
);
view.simulate('click');
expect(onClickMock).toBeCalledWith(false);
});

View file

@ -0,0 +1,79 @@
import { withColors } from '@roleypoly/design-system/atoms/colors/withColors';
import * as React from 'react';
import styled from 'styled-components';
import { roleCategory } from '../../fixtures/storyData';
import { Role as RoleComponent } from './Role';
export default {
title: 'Atoms/Role',
component: RoleComponent,
decorators: [withColors],
};
const Demo = styled.div`
display: flex;
flex-wrap: wrap;
`;
const RoleWithState = (props: any) => {
const [selected, updateSelected] = React.useState(false);
return (
<div style={{ padding: 5 }}>
<RoleComponent
{...props}
selected={selected}
onClick={(next) => updateSelected(next)}
/>
</div>
);
};
export const Role = () => (
<Demo>
{roleCategory.map((c, idx) => (
<RoleWithState key={idx} role={c} />
))}
</Demo>
);
export const Selected = () => (
<Demo>
{roleCategory.map((c, idx) => (
<RoleComponent key={idx} role={c} selected={true} />
))}
</Demo>
);
export const Unselected = () => (
<Demo>
{roleCategory.map((c, idx) => (
<RoleComponent key={idx} role={c} selected={false} />
))}
</Demo>
);
export const DisabledByPosition = () => (
<Demo>
{roleCategory.map((c, idx) => (
<RoleComponent
key={idx}
role={{ ...c, safety: 1 }}
selected={false}
disabled
/>
))}
</Demo>
);
export const DisabledByDanger = () => (
<Demo>
{roleCategory.map((c, idx) => (
<RoleComponent
key={idx}
role={{ ...c, safety: 2 }}
selected={false}
disabled
/>
))}
</Demo>
);

View file

@ -0,0 +1,82 @@
import { palette } from '@roleypoly/design-system/atoms/colors';
import { transitions } from '@roleypoly/design-system/atoms/timings';
import styled, { css } from 'styled-components';
export type StyledProps = {
selected: boolean;
defaultColor: boolean;
disabled: boolean;
type?: 'delete';
};
export const Circle = styled.div<StyledProps>`
width: 24px;
height: 24px;
border-radius: 25px;
background-color: ${(props) =>
props.defaultColor && !props.selected ? 'transparent' : 'var(--role-color)'};
border: 1px solid
${(props) =>
props.defaultColor
? 'var(--role-color)'
: props.selected
? 'var(--role-accent)'
: 'transparent'};
display: flex;
justify-content: center;
align-items: center;
transition: border ${transitions.in2in}s ease-in-out,
background-color ${transitions.in2in}s ease-in-out;
flex-shrink: 0;
svg {
width: 10px;
height: 10px;
fill-opacity: ${(props) => (props.selected || props.disabled ? 1 : 0)};
transition: fill-opacity ${transitions.in2in}s ease-in-out;
fill: ${(props) =>
props.disabled && props.defaultColor
? 'var(--role-color)'
: 'var(--role-contrast)'};
}
`;
export const Outer = styled.div<StyledProps>`
border-radius: 24px;
background-color: ${(props) =>
props.selected && !props.defaultColor ? 'var(--role-color)' : palette.taupe100};
color: ${(props) => (props.selected ? 'var(--role-contrast)' : palette.grey600)};
transition: color ${transitions.in2in}s ease-in-out,
background-color ${transitions.in2in}s ease-in-out,
transform ${transitions.actionable}s ease-in-out,
box-shadow ${transitions.actionable}s ease-in-out;
display: flex;
padding: 4px;
user-select: none;
overflow: hidden;
cursor: pointer;
${(props) =>
!props.disabled
? css`
&:hover {
transform: translateY(-2px);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
${Circle} svg {
fill-opacity: 1;
}
}
&:active {
transform: translateY(0);
box-shadow: 0 0 0 transparent;
}
`
: null};
`;
export const Text = styled.div`
padding: 0 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;

View file

@ -0,0 +1,100 @@
import { numberToChroma } from '@roleypoly/design-system/atoms/colors';
import { evaluatePermission, permissions } from '@roleypoly/misc-utils/hasPermission';
import { Role as RPCRole, RoleSafety } from '@roleypoly/types';
import chroma from 'chroma-js';
import * as React from 'react';
import { FaCheck, FaTimes } from 'react-icons/fa';
import * as styled from './Role.styled';
type Props = {
role: RPCRole;
selected: boolean;
disabled?: boolean;
onClick?: (newState: boolean) => void;
tooltipId?: string;
type?: 'delete';
};
const getColorsFromBase = (baseColor: chroma.Color, contrastCheckThrow: number = 5) => {
// Which has more contrast? Stepping up or stepping down?
const contrastColorUp = baseColor.brighten(contrastCheckThrow);
const contrastColorDown = baseColor.darken(contrastCheckThrow);
if (
chroma.contrast(baseColor, contrastColorUp) >
chroma.contrast(baseColor, contrastColorDown)
) {
return {
contrastColor: baseColor.brighten(3),
accentColor: baseColor.brighten(2),
};
} else {
return {
contrastColor: baseColor.darken(3),
accentColor: baseColor.darken(2),
};
}
};
export const Role = (props: Props) => {
const colorVars = {
'--role-color': 'white',
'--role-contrast': 'hsl(0,0,0%)',
'--role-accent': 'hsl(0,0,70%)',
};
if (props.role.color !== 0) {
const baseColor = numberToChroma(props.role.color);
const { accentColor, contrastColor } = getColorsFromBase(baseColor, 5);
colorVars['--role-color'] = baseColor.css();
colorVars['--role-accent'] = accentColor.css();
colorVars['--role-contrast'] = contrastColor.css();
}
const styledProps: styled.StyledProps = {
selected: props.selected,
defaultColor: props.role.color === 0,
disabled: !!props.disabled,
type: props.type,
};
const extra = !props.disabled
? {}
: {
'data-tip': disabledReason(props.role),
'data-for': props.tooltipId,
};
return (
<styled.Outer
{...styledProps}
style={colorVars as any}
onClick={() => !props.disabled && props.onClick?.(!props.selected)}
{...extra}
>
<styled.Circle {...styledProps}>
{!props.disabled && props.type !== 'delete' ? <FaCheck /> : <FaTimes />}
</styled.Circle>
<styled.Text>{props.role.name}</styled.Text>
</styled.Outer>
);
};
const disabledReason = (role: RPCRole) => {
switch (role.safety) {
case RoleSafety.HigherThanBot:
return `This role is above Roleypoly's own role.`;
case RoleSafety.DangerousPermissions:
const rolePermissions = BigInt(role.permissions);
let permissionHits: string[] = [];
evaluatePermission(rolePermissions, permissions.ADMINISTRATOR) &&
permissionHits.push('Administrator');
evaluatePermission(rolePermissions, permissions.MANAGE_ROLES) &&
permissionHits.push('Manage Roles');
return `This role has unsafe permissions: ${permissionHits.join(', ')}`;
default:
return `This role is disabled.`;
}
};

View file

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