Browse Source

feat: pagination, feedback, remove action

fix/enable-imgix
Thomas Osmonson 4 years ago
parent
commit
d2284f8c78
  1. 11
      .eslintrc.js
  2. 54
      .github/workflows/deploy.yml
  3. 37
      .github/workflows/main.yml
  4. 6
      package.json
  5. 1
      src/common/hooks/use-fathom.ts
  6. 2
      src/components/clarity-ref.tsx
  7. 28
      src/components/feedback.tsx
  8. 33
      src/components/footer.tsx
  9. 120
      src/components/icons/feedback.tsx
  10. 2
      src/components/layouts/docs-layout.tsx
  11. 14
      src/components/mdx/components.tsx
  12. 274
      src/components/pagination.tsx
  13. 5
      src/pages/404.tsx
  14. 4
      src/pages/org/wallet-intro.md
  15. 10
      tsconfig.json
  16. 698
      yarn.lock

11
.eslintrc.js

@ -2,6 +2,7 @@ module.exports = {
extends: ['@blockstack/eslint-config'],
parser: '@typescript-eslint/parser',
parserOptions: {
createDefaultProgram: true,
project: './tsconfig.json',
},
env: {
@ -15,4 +16,14 @@ module.exports = {
context: true,
jestPuppeteer: true,
},
rules: {
'@typescript-eslint/no-unsafe-assignment': 0,
'@typescript-eslint/no-unsafe-member-access': 0,
'@typescript-eslint/no-unsafe-call': 0,
'@typescript-eslint/no-unsafe-return': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/ban-ts-comment': 0,
'@typescript-eslint/ban-ts-ignore': 0,
'@typescript-eslint/restrict-template-expressions': 0,
},
};

54
.github/workflows/deploy.yml

@ -1,54 +0,0 @@
name: Deploy preview
on: pull_request
jobs:
vercel-deployment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Vercel action
uses: amondnet/vercel-action@master
id: vercel-deployment-preview
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
scope: ${{ secrets.VERCEL_SCOPE }}
- name: Comment on PR
uses: actions/github-script@v2
id: comment
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const firstLine = `The Blockstack docs have been deployed with Vercel using the code from this PR!`;
const { data } = await github.issues.listComments({
...context.repo,
issue_number: context.issue.number,
});
const vercelPreviewURLComment = data.find((comment) =>
comment.body.includes(firstLine)
);
const commentId = vercelPreviewURLComment && vercelPreviewURLComment.id || undefined;
const commentBody = `
#### Deploy previews
${firstLine}
- [Blockstack docs](${{ steps.vercel-deployment-preview.outputs.preview-url }})
Built with commit ${context.sha}.
`;
if(context.issue.number){
if (commentId) {
await github.issues.updateComment({
...context.repo,
comment_id: commentId,
body: commentBody,
});
} else {
await github.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body: commentBody,
});
}
}

37
.github/workflows/main.yml

@ -0,0 +1,37 @@
name: Code quality
on: [push]
jobs:
code_quality:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set Node Version
uses: actions/setup-node@v1.4.2
with:
node-version: 14
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Check Cache
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install deps
run: yarn --frozen-lockfile
if: steps.yarn-cache.outputs.cache-hit != 'true'
- name: Lint
run: yarn lint
- name: Typecheck
run: yarn typecheck

6
package.json

@ -20,6 +20,7 @@
"algoliasearch": "^4.3.0",
"babel-plugin-macros": "^2.8.0",
"docsearch.js": "^2.6.3",
"eslint": "^7.4.0",
"fathom-client": "^3.0.0",
"front-matter": "^4.0.2",
"fs-extra": "^9.0.1",
@ -68,6 +69,7 @@
"use-events": "^1.4.2"
},
"devDependencies": {
"@blockstack/eslint-config": "^1.0.5",
"@blockstack/prettier-config": "^0.0.6",
"@next/bundle-analyzer": "^9.4.4",
"babel-plugin-styled-components": "^1.10.7",
@ -90,7 +92,9 @@
"lint:eslint": "eslint \"src/**/*.{ts,tsx}\" -f unix",
"lint:fix": "eslint \"src/**/*.{ts,tsx}\" -f unix --fix",
"lint:prettier": "prettier --check \"src/**/*.{ts,tsx,md,mdx}\" *.js",
"lint:prettier:fix": "prettier --write \"src/**/*.{ts,tsx,md,mdx}\" *.js"
"lint:prettier:fix": "prettier --write \"src/**/*.{ts,tsx,md,mdx}\" *.js",
"typecheck": "tsc --noEmit",
"typecheck:watch": "npm run typecheck -- --watch"
},
"resolutions": {
"preact": "^10.4.4"

1
src/common/hooks/use-fathom.ts

@ -2,6 +2,7 @@ import { useEffect } from 'react';
import { useRouter } from 'next/router';
import * as Fathom from 'fathom-client';
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useFathom = () => {
const router = useRouter();

2
src/components/clarity-ref.tsx

@ -45,7 +45,7 @@ export const ClarityFunctionReference = () =>
<MDXComponents.h4 id={slugify(entry.name) + '-example'}>Example</MDXComponents.h4>
<MDXComponents.pre>
{/* @ts-ignore*/}
<MDXComponents.code children={entry.example.toString() as string} />
<MDXComponents.code children={entry.example.toString()} />
</MDXComponents.pre>
</React.Fragment>
);

28
src/components/feedback.tsx

@ -0,0 +1,28 @@
import React from 'react';
import { Box, BoxProps, color, Flex, space, Stack, themeColor } from '@blockstack/ui';
import { MDXComponents } from '@components/mdx';
import { SadIcon, NeutralIcon, HappyIcon } from '@components/icons/feedback';
import { useHover } from 'use-events';
import { border } from '@common/utils';
const Icon: React.FC<BoxProps & { icon: React.FC<any> }> = ({ icon: IconComponent, ...props }) => {
const [isHovered, bind] = useHover();
return (
<Box _hover={{ color: color('accent'), cursor: 'pointer' }} size="42px" {...props} {...bind}>
<IconComponent bg={isHovered ? themeColor('blue.200') : themeColor('ink.200')} />
</Box>
);
};
export const FeedbackSection: React.FC<BoxProps> = props => {
return (
<Box borderTop={border()} py={space('extra-loose')} mt={space('extra-loose')}>
<MDXComponents.h3>Was this page helpful?</MDXComponents.h3>
<Stack isInline spacing={space('base-loose')} mt={space('base-loose')}>
<Icon icon={SadIcon} />
<Icon icon={NeutralIcon} />
<Icon icon={HappyIcon} />
</Stack>
</Box>
);
};

33
src/components/footer.tsx

@ -1,31 +1,16 @@
import { Box, Flex } from '@blockstack/ui';
import { Text } from '@components/typography';
import React from 'react';
import { Pagination } from './pagination';
import { Link } from '@components//mdx';
import { Pagination } from '@components/pagination';
import { Section, SectionWrapper } from '@components/home/common';
import { FeedbackSection } from '@components/feedback';
const Footer = ({ hidePagination, ...rest }: any) => {
return (
<>
{!hidePagination && <Pagination />}
{/*<Flex*/}
{/* borderTop="1px solid"*/}
{/* borderColor="var(--colors-border)"*/}
{/* textStyle="body.small.medium"*/}
{/* color="ink.400"*/}
{/* p="base"*/}
{/* {...rest}*/}
{/*>*/}
{/* <Box>*/}
{/* <Text>Blockstack Design System</Text>*/}
{/* </Box>*/}
{/* <Box ml="auto">*/}
{/* <Link as="a" href="https://blockstack.org">*/}
{/* Blockstack PBC*/}
{/* </Link>*/}
{/* </Box>*/}
{/*</Flex>*/}
</>
<Section>
<SectionWrapper>
<Pagination />
<FeedbackSection />
</SectionWrapper>
</Section>
);
};

120
src/components/icons/feedback.tsx

@ -0,0 +1,120 @@
import React from 'react';
import { Box, BoxProps, transition } from '@blockstack/ui';
import { SVGProps } from 'react';
export type SvgProps = React.FC<BoxProps & SVGProps<any>>;
export const SadIcon: SvgProps = ({ bg = '#E1E3E8', ...props }) => (
<Box
as="svg"
width="28"
height="28"
viewBox="0 0 28 28"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<Box
as="circle"
transition={transition}
// @ts-ignore
cx="14"
cy="14"
r="14"
fill={bg as string}
/>
<path
d="M5.83334 8.75V8.75C6.94335 10.415 9.39 10.415 10.5 8.75V8.75"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
d="M17.5 8.75V8.75C18.61 10.415 21.0567 10.415 22.1667 8.75V8.75"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
d="M7 17.5001V17.5001C11.055 14.1209 16.945 14.1209 21 17.5001V17.5001"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</Box>
);
export const NeutralIcon: SvgProps = ({ bg = '#E1E3E8', ...props }) => (
<Box
as="svg"
width="28"
height="28"
viewBox="0 0 28 28"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<Box
as="circle"
transition={transition}
// @ts-ignore
cx="14"
cy="14"
r="14"
fill={bg as string}
/>
<path
d="M5.83331 9.91659V9.91659C7.38689 10.0276 8.9464 10.0276 10.5 9.91659V9.91659"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
d="M17.5 9.91659V9.91659C19.0536 10.0276 20.6131 10.0276 22.1667 9.91659V9.91659"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path d="M7 17.5H14H21" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
</Box>
);
export const HappyIcon: SvgProps = ({ bg = '#E1E3E8', ...props }) => (
<Box
as="svg"
width="28"
height="28"
viewBox="0 0 28 28"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<Box
as="circle"
transition={transition} // @ts-ignore
cx="14"
cy="14"
r="14"
fill={bg as string}
/>
<path
d="M5.83331 10.5V10.5C6.94332 8.83498 9.38997 8.83498 10.5 10.5V10.5"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
d="M17.5 10.5V10.5C18.61 8.83498 21.0567 8.83498 22.1667 10.5V10.5"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
d="M21 14C21 15.8565 20.2625 17.637 18.9497 18.9497C17.637 20.2625 15.8565 21 14 21C12.1435 21 10.363 20.2625 9.05025 18.9497C7.7375 17.637 7 15.8565 7 14L14 14H21Z"
fill="currentColor"
/>
</Box>
);

2
src/components/layouts/docs-layout.tsx

@ -271,7 +271,7 @@ const DocsLayout: React.FC<{ isHome?: boolean }> = ({ children, isHome }) => {
{children}
</Flex>
</Main>
<Footer justifySelf="flex-end" />
{!isErrorPage && <Footer justifySelf="flex-end" />}
</Flex>
</Flex>
</Flex>

14
src/components/mdx/components.tsx

@ -196,7 +196,8 @@ const LinkButton = React.memo(({ link, onClick, ...rest }: BoxProps & { link: st
});
// this is to adjust the offset of where the page scrolls to when an anchor is present
const AnchorOffset = ({ id }: BoxProps) => (
const AnchorOffset = ({ id }: BoxProps) =>
id ? (
<Box
as="span"
display="block"
@ -205,7 +206,7 @@ const AnchorOffset = ({ id }: BoxProps) => (
top="-120px"
id={id}
/>
);
) : null;
const Hashtag = () => (
<Box position="absolute" as="span" left="10px" color={color('text-caption')}>
@ -232,24 +233,23 @@ export const Heading = ({ as, children, id, ...rest }: FlexProps) => {
...baseTypeStyles,
...styles,
color: isActive ? color('accent') : (color('text-title') as any),
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
alignItems: 'center',
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
position: 'relative',
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
display: 'flex',
// @ts-ignore
justifyContent: 'flex-start',
...rest,
})}
>
<Box as="span" display="inline-block">
{children}
</Box>
<AnchorOffset id={id} />
{isActive && <Hashtag />}
<LinkButton opacity={isHovered ? 1 : 0} onClick={handleLinkClick} link={link} />
{id && isActive && <Hashtag />}
{id && <LinkButton opacity={isHovered ? 1 : 0} onClick={handleLinkClick} link={link} />}
</Title>
);
};

274
src/components/pagination.tsx

@ -1,167 +1,113 @@
import { Box, Flex, FlexProps, color, space, BoxProps } from '@blockstack/ui';
import { Caption, Text } from '@components/typography';
import { useRouter } from 'next/router';
import React from 'react';
import { slugify } from '@common/utils';
import Link from 'next/link';
import { useHover } from 'use-events';
import { ContentWrapper } from '@components/content-wrapper';
import { Box, BoxProps, Flex, Grid, color, space } from '@blockstack/ui';
import routes from '@common/routes';
import { useRouter } from 'next/router';
import { MDXComponents } from '@components/mdx';
import { border } from '@common/utils';
import NextLink from 'next/link';
import { Link } from '@components/typography';
const usePaginateRoutes = () => {
const router = useRouter();
const getRoute = route => router.pathname.includes(route.path);
const getSection = _section => _section.routes.find(getRoute);
const findSectionByTitle = item => item.title === section.title;
const section = routes.find(getSection);
if (!section)
return {
next: undefined,
prev: undefined,
};
const { routes: sectionRoutes } = section;
const sectionIndex: number = routes.findIndex(findSectionByTitle);
const nextSection = routes[sectionIndex + 1];
const prevSection = routes[sectionIndex - 1];
const isFirstSection = sectionIndex === 0;
const isLastSection = sectionIndex === routes.length - 1;
const route = sectionRoutes.find(getRoute);
const routeIndex: number = sectionRoutes.findIndex(getRoute);
const isFirstRouteInSection = routeIndex === 0;
const isLastRouteInSection = routeIndex === sectionRoutes.length - 1;
let next;
let prev;
if (!isLastRouteInSection) {
next = sectionRoutes[routeIndex + 1];
}
if (isLastRouteInSection && !isLastSection) {
next = nextSection?.routes?.length && nextSection?.routes[0];
}
if (!isFirstRouteInSection) {
prev = sectionRoutes[routeIndex - 1];
}
if (isFirstRouteInSection && !isFirstSection) {
prev = prevSection?.routes?.length && prevSection?.routes[0];
}
return { next, prev };
};
// const transition = 'all 0.2s cubic-bezier(0.23, 1, 0.32, 1)';
//
// const Previous = ({ isHovered }: { isHovered: boolean }) => {
// return (
// <Flex>
// <Box
// mr="extra-tight"
// transition={transition}
// transform={isHovered ? 'translateX(-2px)' : 'none'}
// >
// ←
// </Box>
// <Box>
// <Caption>Previous</Caption>
// </Box>
// </Flex>
// );
// };
// const Next = ({ isHovered }: { isHovered: boolean }) => {
// return (
// <Flex justifyContent="flex-end">
// <Box>
// <Caption>Next</Caption>
// </Box>
// <Box
// ml="extra-tight"
// transition={transition}
// transform={isHovered ? 'translateX(2px)' : 'none'}
// >
// →
// </Box>
// </Flex>
// );
// };
//
// interface PaginationLinkProps extends BoxProps {
// slug: string;
// label: string;
// prev?: boolean;
// }
//
// const PaginationLink = ({ slug, label, prev, ...rest }: PaginationLinkProps) => {
// const [isHovered, bindHover] = useHover();
// return (
// <Link href={`/${slug}`} passHref>
// <Box
// color="var(--colors-text-body)"
// _hover={{
// color: 'var(--colors-accent)',
// }}
// as="a"
// py={space('extra-loose')}
// display="block"
// flexGrow={1}
// {...bindHover}
// {...rest}
// >
// <Caption textAlign={prev ? 'left' : 'right'} display="block">
// {prev ? <Previous isHovered={isHovered} /> : <Next isHovered={isHovered} />}
// </Caption>
// <Text
// textAlign={prev ? 'left' : 'right'}
// display="block"
// textStyle="display.large"
// color="currentColor"
// >
// {label}
// </Text>
// </Box>
// </Link>
// );
// };
//
// const getRouteWithSection = (route: string) => {
// let section = '';
// const keys = Object.keys(paginationRoutes);
// keys.forEach(key => {
// const routes = paginationRoutes[key].map(r => slugify(r));
// if (routes.length && routes.find(r => r === route) && key !== 'top' && key !== 'bottom') {
// section = key + '/';
// }
// });
// return section + route;
// };
//
// const usePagination = () => {
// const router = useRouter();
// const [state, setState] = React.useState({
// previous: undefined,
// previousSlug: undefined,
// next: undefined,
// nextSlug: undefined,
// });
// React.useEffect(() => {
// const routesAsSlugs = routes.map(r => slugify(r));
// const _route = router.asPath.replace('/', '');
// const __route = _route.includes('/') ? _route.split('/')[1] : _route;
// const route = __route === '' ? 'getting-started' : __route;
// const index = routesAsSlugs.indexOf(route);
// const previous = routes[index - 1];
// const previousSlug = getRouteWithSection(routesAsSlugs[index - 1]);
// const next = routes[index + 1];
// const nextSlug = getRouteWithSection(routesAsSlugs[index + 1]);
//
// setState({
// previous,
// previousSlug,
// next,
// nextSlug,
// });
// }, [router.asPath]);
//
// const { previous, previousSlug, next, nextSlug } = state;
//
// return [
// { label: previous, path: previousSlug, condition: !!previous },
// {
// label: next,
// path: nextSlug,
// condition: !!next,
// },
// ];
// };
//
// export const Pagination: React.FC<FlexProps> = props => {
// const buttons = usePagination();
// return (
// <Box maxWidth="100%" {...props}>
// <ContentWrapper
// borderTop="1px solid"
// borderColor={color('border')}
// flexDirection="row"
// alignItems="baseline"
// justify="space-between"
// pt="unset"
// pb="unset"
// width="100%"
// maxWidth="98ch"
// mx="auto"
// px={space('extra-loose')}
// >
// {buttons.map((button, index) =>
// button.condition ? (
// <PaginationLink
// textAlign={index === 0 ? 'left' : 'right'}
// slug={button.path}
// label={button.label}
// prev={index === 0}
// key={index}
// />
// ) : null
// )}
// </ContentWrapper>
// </Box>
// );
// };
const Description: React.FC<BoxProps> = props => (
<Box maxWidth="32ch" mt={space('extra-tight')}>
<MDXComponents.p {...props} />
</Box>
);
export const Pagination: React.FC<FlexProps> = props => <></>;
export const Pagination = ({ hidePagination, ...rest }: any) => {
const { next, prev } = usePaginateRoutes();
return (
<Grid
pt={space('extra-loose')}
borderTop={border()}
gridColumnGap={space('base-loose')}
gridRowGap={space('extra-loose')}
gridTemplateColumns={['repeat(1, 1fr)', 'repeat(1, 1fr)', 'repeat(2, 1fr)', 'repeat(2, 1fr)']}
>
{prev ? (
<Box _hover={{ cursor: 'pointer' }} width="100%" position="relative">
<NextLink href={`/${prev.path}`} passHref>
<Link position="absolute" size="100%" zIndex={2} as="a" />
</NextLink>
<MDXComponents.h5 color={color('text-caption')}>Previous</MDXComponents.h5>
<Box maxWidth="38ch">
<MDXComponents.h3 my={0}>{prev.title || prev.headings[0]}</MDXComponents.h3>
</Box>
{prev.description && <Description>{prev.description}</Description>}
</Box>
) : (
<Box />
)}
{next ? (
<Flex
_hover={{ cursor: 'pointer' }}
width="100%"
textAlign="right"
direction="column"
align="flex-end"
position="relative"
>
<NextLink href={`/${next.path}`} passHref>
<Link position="absolute" size="100%" zIndex={2} as="a" />
</NextLink>
<MDXComponents.h5 color={color('text-caption')} width="100%" display="block">
Next
</MDXComponents.h5>
<Box maxWidth="38ch">
<MDXComponents.h3 my={0}>{next.title || next.headings[0]}</MDXComponents.h3>
</Box>
{next.description && <Description>{next.description}</Description>}
</Flex>
) : (
<Box />
)}
</Grid>
);
};

5
src/pages/404.tsx

@ -4,7 +4,6 @@ import { ContentWrapper } from '@components/content-wrapper';
import { Text, Title, Link } from '@components/typography';
import FileDocumentEditOutlineIcon from 'mdi-react/FileDocumentEditOutlineIcon';
import { border } from '@common/utils';
import { DocsLayout } from '@components/layouts/docs-layout';
import Head from 'next/head';
const toPx = (number: number): string => `${number}px`;
@ -45,7 +44,7 @@ const Graphic = (props: FlexProps) => {
const NotFoundPage = () => {
return (
<DocsLayout>
<>
<Head>
<title>Page not found | Blockstack UI</title>
</Head>
@ -73,7 +72,7 @@ const NotFoundPage = () => {
</Box>
</Stack>
</ContentWrapper>
</DocsLayout>
</>
);
};

4
src/pages/org/wallet-intro.md

@ -34,8 +34,8 @@ uses is not recorded.
> ##### Security tip: What to share and what not to
>
> A Stacks address is a string of letters and numbers starting with an `SP` or `SM`, like
`SM3KJBA4RZ7Z20KD2HBXNSXVPCR1D3CRAV6Q05MKT`. You can and should share the address when you want
someone to send STX tokens to you.
> `SM3KJBA4RZ7Z20KD2HBXNSXVPCR1D3CRAV6Q05MKT`. You can and should share the address when you want
> someone to send STX tokens to you.
>
> Your seed phrase, 24 words in an ordered sequence, is the private key for your addresses and wallet. **never** share your seed phrase with anyone.

10
tsconfig.json

@ -22,6 +22,14 @@
"baseUrl": "src"
},
"exclude": ["node_modules", ".next"],
"include": ["next-env.d.ts", "src/**/*.ts", "src/**/*.tsx", "src/**/*.mdx", "types/**/*.d.ts"],
"include": [
"**/src/**/*.ts",
"**/test/**/*.ts",
"next-env.d.ts",
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.mdx",
"types/**/*.d.ts"
],
"noImplicitAny": false
}

698
yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save