Skills Selector (#124)

* skills map in context

* using react-select

* removed sudo from package.json

* fixed lint and format issues
This commit is contained in:
dan13ram
2020-09-30 08:51:13 +05:30
committed by GitHub
parent 375c39b133
commit d5844dd8db
13 changed files with 264 additions and 22 deletions

View File

@@ -1,5 +1,6 @@
{
"printWidth": 80,
"singleQuote": true,
"trailingComma": "all"
"trailingComma": "all",
"arrowParens": "always"
}

View File

@@ -39,6 +39,8 @@ services:
dockerfile: ./docker/backend/Dockerfile
target: base
command: yarn backend:dev
ports:
- 4000:4000
volumes:
- ./packages/@types:/usr/src/app/packages/@types
- ./packages/backend:/usr/src/app/packages/backend

View File

@@ -51,6 +51,7 @@
"@types/node": "^14.6.4",
"@types/react": "^16.9.43",
"@types/react-dom": "^16.9.8",
"@types/react-select": "^3.0.20",
"@typescript-eslint/eslint-plugin": "3.9.1",
"@typescript-eslint/parser": "3.6.0",
"eslint": "6.8.0",

View File

@@ -25,8 +25,9 @@
},
"dependencies": {
"@chakra-ui/core": "next",
"@chakra-ui/icons": "next",
"@chakra-ui/theme": "next",
"@chakra-ui/icons": "next"
"react-select": "^3.1.0"
},
"devDependencies": {
"@babel/core": "^7.10.5",

View File

@@ -0,0 +1,73 @@
import React from 'react';
import Select, { Props as SelectProps, Styles } from 'react-select';
import { theme } from './theme';
const selectStyles: Styles = {
menu: (styles) => ({
...styles,
background: theme.colors.purple[400],
}),
noOptionsMessage: (styles) => ({
...styles,
color: theme.colors.white,
}),
input: (styles) => ({
...styles,
color: theme.colors.white,
}),
groupHeading: (styles) => ({
...styles,
color: theme.colors.white,
}),
option: (styles) => ({
...styles,
background: theme.colors.dark,
':hover': {
backgroundColor: theme.colors.purpleTag,
color: theme.colors.white,
},
}),
control: (styles) => ({
...styles,
background: theme.colors.dark,
border: theme.colors.dark,
}),
multiValue: (styles) => ({
...styles,
background: theme.colors.purpleTag,
color: theme.colors.white,
}),
multiValueLabel: (styles) => ({
...styles,
background: theme.colors.purpleTag,
color: theme.colors.white,
}),
multiValueRemove: (styles) => ({
...styles,
color: theme.colors.white,
cursor: 'pointer',
':hover': {
color: theme.colors.blueLight,
},
}),
clearIndicator: (styles) => ({
...styles,
color: theme.colors.white,
cursor: 'pointer',
':hover': {
color: theme.colors.blueLight,
},
}),
dropdownIndicator: (styles) => ({
...styles,
color: theme.colors.white,
cursor: 'pointer',
':hover': {
color: theme.colors.blueLight,
},
}),
};
export const SelectSearch: React.FC<SelectProps> = (props) => (
<Select styles={selectStyles} {...props} />
);

View File

@@ -40,3 +40,4 @@ export { MetaBox } from './MetaBox';
export { MetaTag } from './MetaTag';
export { H1, P } from './typography';
export { ResponsiveText } from './ResponsiveText';
export { SelectSearch } from './SelectSearch';

View File

@@ -3,6 +3,8 @@ import { FlexContainer } from 'components/Container';
import { SetupContext } from 'contexts/SetupContext';
import React, { useContext } from 'react';
import { SetupSkills } from './SetupSkills';
export const SetupProfession: React.FC = () => {
const { useProgress } = useContext(SetupContext);
const numProgressSteps = 3;
@@ -10,9 +12,7 @@ export const SetupProfession: React.FC = () => {
return (
<FlexContainer flex={1}>
{currentProgress === 0 && (
<MetaHeading mb={10}>What are your superpowers?</MetaHeading>
)}
{currentProgress === 0 && <SetupSkills />}
{currentProgress === 1 && <MetaHeading mb={10}>Availability</MetaHeading>}
{currentProgress === 2 && <MetaHeading mb={10}>Memberships</MetaHeading>}
<MetaButton onClick={onNextPress}>Next Step</MetaButton>

View File

@@ -0,0 +1,22 @@
import { MetaHeading, SelectSearch } from '@metafam/ds';
import { FlexContainer } from 'components/Container';
import { SetupContext } from 'contexts/SetupContext';
import React, { useContext } from 'react';
import { SkillOption } from 'utils/skillHelpers';
export const SetupSkills: React.FC = () => {
const { skillsList, skills, setSkills } = useContext(SetupContext);
return (
<FlexContainer mb={10} align="stretch">
<MetaHeading mb={10}>What are your superpowers?</MetaHeading>
<SelectSearch
isMulti
value={skills}
onChange={(value) => setSkills(value as Array<SkillOption>)}
options={skillsList}
placeholder="ADD YOUR SKILLS"
/>
</FlexContainer>
);
};

View File

@@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
import { CategoryOption, SkillOption } from 'utils/skillHelpers';
type SetupContextType = {
useProgress: (numProgressSteps: number) => [number, () => void];
@@ -7,6 +8,9 @@ type SetupContextType = {
setStep: React.Dispatch<React.SetStateAction<number>>;
setProgress: React.Dispatch<React.SetStateAction<number>>;
numTotalSteps: number;
skills: Array<SkillOption>;
setSkills: React.Dispatch<React.SetStateAction<Array<SkillOption>>>;
skillsList: Array<CategoryOption>;
};
export const SetupContext = React.createContext<SetupContextType>({
@@ -16,9 +20,19 @@ export const SetupContext = React.createContext<SetupContextType>({
setStep: () => undefined,
setProgress: () => undefined,
numTotalSteps: 0,
skills: [],
setSkills: () => undefined,
skillsList: [],
});
export const SetupContextProvider: React.FC = ({ children }) => {
type Props = {
skillsList: Array<CategoryOption>;
};
export const SetupContextProvider: React.FC<Props> = ({
children,
skillsList,
}) => {
const [step, setStep] = useState<number>(0);
const [progress, setProgress] = useState<number>(0.5);
const numTotalSteps = 3;
@@ -44,6 +58,8 @@ export const SetupContextProvider: React.FC = ({ children }) => {
return [currentProgress, onNextPress];
};
const [skills, setSkills] = useState<Array<SkillOption>>([]);
return (
<SetupContext.Provider
value={{
@@ -53,6 +69,9 @@ export const SetupContextProvider: React.FC = ({ children }) => {
setStep,
setProgress,
numTotalSteps,
skills,
setSkills,
skillsList,
}}
>
{children}

View File

@@ -0,0 +1,38 @@
import gql from 'fake-tag';
import { GetSkillsQuery } from './autogen/types';
import { client } from './client';
const skillsQuery = gql`
query GetSkills {
Skill(
order_by: { Player_Skills_aggregate: { count: desc }, category: asc }
) {
id
name
category
}
}
`;
export interface Skill {
id: string;
name: string;
category: string;
}
export const getSkills = async (): Promise<Skill[]> => {
const { data, error } = await client
.query<GetSkillsQuery>(skillsQuery)
.toPromise();
if (!data) {
if (error) {
throw error;
}
return [];
}
return data.Skill;
};

View File

@@ -4,16 +4,11 @@ import { SetupHeader } from 'components/Setup/SetupHeader';
import { SetupPersonality } from 'components/Setup/SetupPersonality';
import { SetupProfession } from 'components/Setup/SetupProfession';
import { SetupContext, SetupContextProvider } from 'contexts/SetupContext';
import { getSkills } from 'graphql/getSkills';
import { InferGetStaticPropsType } from 'next';
import BackgroundImage from 'public/images/profile-background.jpg';
import React, { useContext } from 'react';
export const getStaticProps = async () => {
return {
props: {
hidePageHeader: true,
},
};
};
import { parseSkills } from 'utils/skillHelpers';
const ProfileSetup: React.FC = () => {
const { step, numTotalSteps } = useContext(SetupContext);
@@ -27,8 +22,22 @@ const ProfileSetup: React.FC = () => {
);
};
const ProfileSetupWithContext: React.FC = () => (
<SetupContextProvider>
export const getStaticProps = async () => {
const skills = await getSkills();
const skillsList = parseSkills(skills);
return {
props: {
skillsList,
hidePageHeader: true,
},
};
};
export type Props = InferGetStaticPropsType<typeof getStaticProps>;
const ProfileSetupWithContext: React.FC<Props> = ({ skillsList }) => (
<SetupContextProvider skillsList={skillsList}>
<ProfileSetup />
</SetupContextProvider>
);

View File

@@ -0,0 +1,33 @@
import { Skill } from 'graphql/getSkills';
export type SkillMap = {
[category: string]: CategoryOption;
};
export type SkillOption = Skill & {
value: string;
label: string;
};
export type CategoryOption = {
label: string;
options: Array<SkillOption>;
};
export const parseSkills = (skills: Array<Skill>): Array<CategoryOption> => {
const skillsMap: SkillMap = {};
skills.map((skill) => {
if (!(skill.category in skillsMap)) {
skillsMap[skill.category] = {
label: skill.category,
options: [],
};
}
return skillsMap[skill.category].options?.push({
value: skill.id,
label: skill.name,
...skill,
});
});
return Object.values(skillsMap);
};

View File

@@ -2124,7 +2124,7 @@
resolved "https://registry.yarnpkg.com/@egoist/vue-to-react/-/vue-to-react-1.1.0.tgz#83c884b8608e8ee62e76c03e91ce9c26063a91ad"
integrity sha512-MwfwXHDh6ptZGLEtNLPXp2Wghteav7mzpT2Mcwl3NZWKF814i5hhHnNkVrcQQEuxUroSWQqzxLkMKSb+nhPang==
"@emotion/cache@^10.0.27":
"@emotion/cache@^10.0.27", "@emotion/cache@^10.0.9":
version "10.0.29"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0"
integrity sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==
@@ -2146,7 +2146,19 @@
"@emotion/sheet" "0.9.4"
"@emotion/utils" "0.11.3"
"@emotion/css@^10.0.27":
"@emotion/core@^10.0.9":
version "10.0.35"
resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.35.tgz#513fcf2e22cd4dfe9d3894ed138c9d7a859af9b3"
integrity sha512-sH++vJCdk025fBlRZSAhkRlSUoqSqgCzYf5fMOmqqi3bM6how+sQpg3hkgJonj8GxXM4WbD7dRO+4tegDB9fUw==
dependencies:
"@babel/runtime" "^7.5.5"
"@emotion/cache" "^10.0.27"
"@emotion/css" "^10.0.27"
"@emotion/serialize" "^0.11.15"
"@emotion/sheet" "0.9.4"
"@emotion/utils" "0.11.3"
"@emotion/css@^10.0.27", "@emotion/css@^10.0.9":
version "10.0.27"
resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c"
integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==
@@ -6115,7 +6127,7 @@
"@types/history" "*"
"@types/react" "*"
"@types/react-dom@^16.8.3", "@types/react-dom@^16.9.8":
"@types/react-dom@*", "@types/react-dom@^16.8.3", "@types/react-dom@^16.9.8":
version "16.9.8"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423"
integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==
@@ -6146,6 +6158,15 @@
"@types/history" "*"
"@types/react" "*"
"@types/react-select@^3.0.20":
version "3.0.20"
resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-3.0.20.tgz#1cbedcb145a910602ef192698235cd2d9debdd04"
integrity sha512-yKFcq773jsUV5C1nYb0KeQCboo8tCo4ngcM3mi9fmC0CTGJeIuV/mfxaWtmv7i7HetQtki4p7h1YpP8wBy9gWw==
dependencies:
"@types/react" "*"
"@types/react-dom" "*"
"@types/react-transition-group" "*"
"@types/react-syntax-highlighter@11.0.4":
version "11.0.4"
resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz#d86d17697db62f98046874f62fdb3e53a0bbc4cd"
@@ -6160,7 +6181,7 @@
dependencies:
"@types/react" "*"
"@types/react-transition-group@4.4.0", "@types/react-transition-group@^4.2.0":
"@types/react-transition-group@*", "@types/react-transition-group@4.4.0", "@types/react-transition-group@^4.2.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d"
integrity sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==
@@ -19240,7 +19261,7 @@ memdown@~3.0.0:
ltgt "~2.2.0"
safe-buffer "~5.1.1"
memoize-one@5.1.1:
memoize-one@5.1.1, memoize-one@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
@@ -23499,6 +23520,13 @@ react-icons@^3.11.0, react-icons@^3.9.0:
dependencies:
camelcase "^5.0.0"
react-input-autosize@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.2.tgz#fcaa7020568ec206bc04be36f4eb68e647c4d8c2"
integrity sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==
dependencies:
prop-types "^15.5.8"
react-inspector@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-4.0.1.tgz#0f888f78ff7daccbc7be5d452b20c96dc6d5fbb8"
@@ -23687,6 +23715,20 @@ react-scripts@3.4.3:
optionalDependencies:
fsevents "2.1.2"
react-select@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.1.0.tgz#ab098720b2e9fe275047c993f0d0caf5ded17c27"
integrity sha512-wBFVblBH1iuCBprtpyGtd1dGMadsG36W5/t2Aj8OE6WbByDg5jIFyT7X5gT+l0qmT5TqWhxX+VsKJvCEl2uL9g==
dependencies:
"@babel/runtime" "^7.4.4"
"@emotion/cache" "^10.0.9"
"@emotion/core" "^10.0.9"
"@emotion/css" "^10.0.9"
memoize-one "^5.0.0"
prop-types "^15.6.0"
react-input-autosize "^2.2.2"
react-transition-group "^4.3.0"
react-sizeme@^2.6.7:
version "2.6.12"
resolved "https://registry.yarnpkg.com/react-sizeme/-/react-sizeme-2.6.12.tgz#ed207be5476f4a85bf364e92042520499455453e"
@@ -23743,7 +23785,7 @@ react-textarea-autosize@^7.1.0:
"@babel/runtime" "^7.1.2"
prop-types "^15.6.0"
react-transition-group@4.4.1, react-transition-group@^4.4.0, react-transition-group@^4.4.1:
react-transition-group@4.4.1, react-transition-group@^4.3.0, react-transition-group@^4.4.0, react-transition-group@^4.4.1:
version "4.4.1"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==