Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: render roadmaps with d3 #358

Merged
merged 78 commits into from
May 19, 2023
Merged
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
f57560d
chore: fix error when clicking roadmap item in d3 mode
SgtPooki Jan 31, 2023
dc8b3fc
chore: render d3 roadmap with correct header
SgtPooki Jan 31, 2023
6d5930d
chore: pull out date logic
SgtPooki Jan 31, 2023
1127481
feat: get d3 mode rendering
SgtPooki Jan 31, 2023
e4d92e2
chore: ensure today line toggles properly
SgtPooki Jan 31, 2023
d18f79d
chore: roadmap header ticks&labels are rendering
SgtPooki Jan 31, 2023
7321f38
tmp
SgtPooki Jan 31, 2023
4091815
tmp: attempting panning impl and quit. started on collision detection
SgtPooki Feb 1, 2023
81cbfa2
chore: expand mode=d3 milestone items width to match text
SgtPooki Feb 1, 2023
6cafa60
chore(d3): start work on collision detection
SgtPooki Feb 1, 2023
8b4d56f
Merge branch 'main' into 237-enhancement-bug-migrate-rendering-to-d3
SgtPooki Apr 25, 2023
2d82e4b
feat: bin-packing algorithm implemented for d3 rendering
SgtPooki Apr 25, 2023
39ef5c3
feat: bin-packing is working really well
SgtPooki Apr 25, 2023
9372b59
feat: d3 panning works
SgtPooki Apr 25, 2023
a3d2900
feat: d3 zooming + panning
SgtPooki Apr 26, 2023
db35ff4
fix: d3 roadmap view width
SgtPooki Apr 27, 2023
036c455
fix: milestone text
SgtPooki Apr 27, 2023
88fe12e
fix(milestone): d3 rendering is more accurate
SgtPooki Apr 27, 2023
22fa68f
fix(milestone): d3 milestones show progress bar
SgtPooki Apr 27, 2023
d166d0e
fix(milestone): text padding, size, polish, truncating
SgtPooki Apr 27, 2023
bc47dd2
fix(drag): prevent unintentional attempt to drag milestone items
SgtPooki Apr 27, 2023
88d349c
fix: d3 roadmap height + panning and zoom polish
SgtPooki Apr 27, 2023
841ec03
fix: date granularity upon zooming out
SgtPooki Apr 28, 2023
dec1040
feat: SERIOUS polish on zoom, pan, & header labels
SgtPooki Apr 28, 2023
3ab4548
tmp: temporarily force rendering of d3 roadmap for preview
SgtPooki Apr 28, 2023
4a052d0
chore: fix build errors
SgtPooki Apr 28, 2023
10c173c
tmp: temporarily ignore unused RoadmapDetailed
SgtPooki Apr 28, 2023
42c84c6
tmp: dont run tests for d3 preview; for now
SgtPooki Apr 28, 2023
a47f5c9
fix: zoom and pan controls are more intuitive
SgtPooki May 1, 2023
177c77f
chore: remove unused component
SgtPooki May 2, 2023
bf102c2
fix: tickGuides stretch to full height
SgtPooki May 2, 2023
73a79c9
fix: invalid foreignObject usage
SgtPooki May 2, 2023
4fb5f72
feat: implement detailed view in d3
SgtPooki May 3, 2023
f8f2f17
fix: build
SgtPooki May 3, 2023
43d408c
tmp: using issueData context
SgtPooki May 4, 2023
12d754a
fix: binPackedGroups doesnt cause infinite re-renders
SgtPooki May 4, 2023
2cf6f6a
fix: pan/zoom work when toggling views
SgtPooki May 4, 2023
85a4c05
fix: use MMM DD, YYYY display format for milestone dates
SgtPooki May 4, 2023
e47f309
chore: pull out RoadmapGroupRenderer
SgtPooki May 4, 2023
8b42fa4
chore: fix build, renable tests, skip e2e
SgtPooki May 4, 2023
02ea9bc
fix: todayLine styling
SgtPooki May 5, 2023
16f290b
chore: remove unused code
SgtPooki May 5, 2023
921b80d
chore: todayLine polish
SgtPooki May 5, 2023
e386ef1
chore: some more cleanup
SgtPooki May 5, 2023
5282a4a
chore: set ETA always to EOD
SgtPooki May 9, 2023
677ae95
fix: rescale the timeScale with zoom transform
SgtPooki May 9, 2023
da3dfff
fix: zoom and panning
SgtPooki May 9, 2023
63aaf26
feat: make zoom/pan level shareable
SgtPooki May 10, 2023
2fecf6a
feat: default zoom finding, and url parameter setting
SgtPooki May 10, 2023
046d7f2
chore: fix build
SgtPooki May 10, 2023
3782932
Merge branch 'main' into 237-enhancement-bug-migrate-rendering-to-d3
SgtPooki May 12, 2023
e36118b
tests: fix unit tests
SgtPooki May 12, 2023
85a46c6
fix: default view finding when all dates are same
SgtPooki May 12, 2023
42e4ff6
fix: d3-migration e2e tests
SgtPooki May 12, 2023
5aaa3ac
fix(lint): remove unused legacyView
SgtPooki May 12, 2023
15d2b05
chore: fix next+eslint
SgtPooki May 12, 2023
e4eca59
chore: remove dead code
SgtPooki May 12, 2023
37795a5
feat: add legacy view button
SgtPooki May 12, 2023
e13c3c2
test: remove tests for removed files
SgtPooki May 12, 2023
be4ba2c
chore(pr-comment): remove redundant null check
SgtPooki May 12, 2023
7f97201
chore(pr-comment): clean up components/roadmap/header.tsx
SgtPooki May 12, 2023
a0d6da6
chore(pr-comment): move constants to svgConstants
SgtPooki May 12, 2023
282f74a
chore(pr-comment): refactor math into ItemContainerSvg
SgtPooki May 13, 2023
804e9a5
chore: implement standard style
SgtPooki May 13, 2023
ee76f16
chore(lint): sort imports
SgtPooki May 13, 2023
321ee77
feat: make roadmapHeader smarter
SgtPooki May 13, 2023
f598b42
feat: make roadmapHeader even smarter (mobile support)
SgtPooki May 13, 2023
17d5f76
chore(pr-comment): code cleanup
SgtPooki May 15, 2023
5c42126
chore: use Dayjs instead of Date in NewRoadMapHeaderTick
SgtPooki May 15, 2023
ed79390
fix: css on hover for clickable milestones
SgtPooki May 15, 2023
3c999d3
chore: remove RoadmapMode and it's uses
SgtPooki May 15, 2023
b748e13
chore: remove unused file
SgtPooki May 15, 2023
cd055ca
fix: some viewMode issues
SgtPooki May 15, 2023
fa90f5c
fix: breadcrumb nav & zoomTransform+url bug
SgtPooki May 15, 2023
e990859
Merge branch 'main' into 237-enhancement-bug-migrate-rendering-to-d3
SgtPooki May 16, 2023
65d7e2b
fix: lint
SgtPooki May 16, 2023
a96992d
fix: :package: Fixing box model (#375)
whizzzkid May 16, 2023
7251e5f
fix: dont loop over each group of binPackedItems (#376)
SgtPooki May 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 32 additions & 15 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,33 @@ module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
project: ['./tsconfig.json']
},
env: {
jest: true,
jest: true
},
plugins: [
'@typescript-eslint',
'import',
'import'
],
extends: [
'plugin:@next/next/recommended',
'next',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'prettier',
'plugin:import/typescript',
'standard'
],
rules: {
camelcase: 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/no-unused-vars': ['error', { 'destructuredArrayIgnorePattern': '^_' }],
'@typescript-eslint/no-unused-vars': ['error', { destructuredArrayIgnorePattern: '^_' }],
'@typescript-eslint/restrict-template-expressions': 'off',
'arrow-body-style': 'off',
'arrow-body-style': 'warn',
'implicit-arrow-linebreak': 'off',
'jsx-a11y/alt-text': 'off',
Expand All @@ -41,16 +42,32 @@ module.exports = {
'react/prop-types': 'off',
'react/react-in-jsx-scope': 'off',
'import/no-unused-modules': [2, {
"unusedExports": true,
"ignoreExports": [
'pages/',
'components/roadmap/',
unusedExports: true,
ignoreExports: [
'hooks/useEffectDebugger.ts',
'lib/backend/saveIssueDataToFile.ts',
'lib/mergeStarMapsErrorGroups.ts',
'lib/addStarMapsErrorsToStarMapsErrorGroups.ts',
'playwright.config.ts',
'pages/',
'playwright.config.ts'
]
}]
}],
'import/order': [
'error',
{
groups: [
'builtin', // Built-in types are first
['external', 'unknown'],
['parent', 'sibling', 'internal', 'index']
],
'newlines-between': 'always',
alphabetize: {
order: 'asc',
caseInsensitive: true
},
warnOnUnassignedImports: true
}
]
},
settings: {
'import/parsers': {
Expand All @@ -60,8 +77,8 @@ module.exports = {
typescript: {},
node: {
extensions: ['.jsx', '.jsx', '.ts', '.tsx'],
moduleDirectory: ['node_modules', 'lib/', 'components/', 'hooks/', 'pages/'],
moduleDirectory: ['node_modules', 'lib/', 'components/', 'hooks/', 'pages/']
}
}
}
};
}
140 changes: 72 additions & 68 deletions components/RoadmapForm.tsx
Original file line number Diff line number Diff line change
@@ -1,119 +1,123 @@
import { Router, useRouter } from 'next/router';
import { Button, FormControl, FormErrorMessage, Input, InputGroup, InputLeftElement, InputRightElement, Text } from '@chakra-ui/react';
import { SearchIcon } from '@chakra-ui/icons'
import React, { useEffect, useState } from 'react';
import { Button, FormControl, FormErrorMessage, Input, InputGroup, InputLeftElement, InputRightElement, Text } from '@chakra-ui/react'
import { isEmpty } from 'lodash'
import { Router, useRouter } from 'next/router'
import React, { useEffect, useState } from 'react'

import { useGlobalLoadingState } from '../hooks/useGlobalLoadingState';
import useCheckMobileScreen from '../hooks/useCheckSmallScreen'
import { setCurrentIssueUrl, useCurrentIssueUrl } from '../hooks/useCurrentIssueUrl'
import { useGlobalLoadingState } from '../hooks/useGlobalLoadingState'
import { useViewMode } from '../hooks/useViewMode'
import { ViewMode } from '../lib/enums'
import { getValidUrlFromInput } from '../lib/getValidUrlFromInput'
import { paramsFromUrl } from '../lib/paramsFromUrl'
import styles from './RoadmapForm.module.css'
import theme from './theme/constants'
import { setCurrentIssueUrl, useCurrentIssueUrl } from '../hooks/useCurrentIssueUrl';
import { paramsFromUrl } from '../lib/paramsFromUrl';
import { getValidUrlFromInput } from '../lib/getValidUrlFromInput';
import { useViewMode } from '../hooks/useViewMode';
import { ViewMode } from '../lib/enums';
import { isEmpty } from 'lodash';
import useCheckMobileScreen from '../hooks/useCheckSmallScreen';

export function RoadmapForm() {
const router = useRouter();
const globalLoadingState = useGlobalLoadingState();
const currentIssueUrl = useCurrentIssueUrl();
const [issueUrl, setIssueUrl] = useState<string | null>();
const [error, setError] = useState<Error | null>(null);
const [isInputBlanked, setIsInputBlanked] = useState<boolean>(false);
const [isSmallScreen] = useCheckMobileScreen();
const viewMode = useViewMode() as ViewMode;
export function RoadmapForm () {
const router = useRouter()
const globalLoadingState = useGlobalLoadingState()
const currentIssueUrl = useCurrentIssueUrl()
const [issueUrl, setIssueUrl] = useState<string | null>()
const [error, setError] = useState<Error | null>(null)
const [isInputBlanked, setIsInputBlanked] = useState<boolean>(false)
const [isSmallScreen] = useCheckMobileScreen()
const viewMode = useViewMode() as ViewMode

useEffect(() => {
if (!isInputBlanked && isEmpty(currentIssueUrl) && window.location.pathname.length > 1) {
try {
const urlObj = getValidUrlFromInput(window.location.pathname.replace('/roadmap', ''));
setCurrentIssueUrl(urlObj.toString());
const urlObj = getValidUrlFromInput(window.location.pathname.replace('/roadmap', ''))
setCurrentIssueUrl(urlObj.toString())
} catch {}
}
}, [currentIssueUrl, isInputBlanked])

useEffect(() => {
const asyncFn = async () => {
if (router.isReady) {
if (!issueUrl) return;
if (!issueUrl) return
try {
const params = paramsFromUrl(issueUrl);
const params = paramsFromUrl(issueUrl)
if (params) {
const { owner, repo, issue_number } = params;
setIssueUrl(null);
const { owner, repo, issue_number } = params
setIssueUrl(null)
if (window.location.pathname.includes(`github.com/${owner}/${repo}/issues/${issue_number}`)) {
setTimeout(() => {
/**
* Clear the error after a few seconds.
*/
setError(null);
}, 5000);
throw new Error('Already viewing this issue');
setError(null)
}, 5000)
throw new Error('Already viewing this issue')
}
await router.push(`/roadmap/github.com/${owner}/${repo}/issues/${issue_number}#${viewMode}`);
globalLoadingState.stop();
await router.push(`/roadmap/github.com/${owner}/${repo}/issues/${issue_number}#view=${viewMode}`)
globalLoadingState.stop()
}
} catch (err) {
setError(err as Error);
globalLoadingState.stop();
setError(err as Error)
globalLoadingState.stop()
}
}
};
asyncFn();
}, [router, issueUrl, viewMode, globalLoadingState]);
}
asyncFn()
}, [router, issueUrl, viewMode, globalLoadingState])

const formSubmit = (e) => {
e.preventDefault();
globalLoadingState.start();
setError(null);
e.preventDefault()
globalLoadingState.start()
setError(null)

try {
if (currentIssueUrl == null) {
throw new Error('currentIssueUrl is null');
throw new Error('currentIssueUrl is null')
}
const newUrl = getValidUrlFromInput(currentIssueUrl);
setIssueUrl(newUrl.toString());
const newUrl = getValidUrlFromInput(currentIssueUrl)
setIssueUrl(newUrl.toString())
} catch (err) {
setError(err as Error);
globalLoadingState.stop();
setError(err as Error)
globalLoadingState.stop()
}
}

const inputRightElement = (
<Button type="submit" isLoading={globalLoadingState.get()} className={styles.formSubmitButton} border="1px solid #8D8D8D" borderRadius="4px" bg="rgba(141, 141, 141, 0.3)" onClick={formSubmit}>
<Button type="submit" isLoading={globalLoadingState.get()} className={styles.formSubmitButton} border="1px solid #8D8D8D" borderRadius="4px" bg="rgba(141, 141, 141, 0.3)" onClick={formSubmit}>
<Text p="6px 10px" color="white">⏎</Text>
</Button>
);
)

const onChangeHandler = (e) => {
setIsInputBlanked(true);
setIsInputBlanked(true)
setCurrentIssueUrl(e.target.value ?? '')
};
}

Router.events.on('routeChangeStart', (...events) => {
globalLoadingState.start();
const path = events[0];
if (path === '/') {
setIsInputBlanked(true);
setCurrentIssueUrl('');
return;
useEffect(() => {
const handleRouteChange = (...events) => {
globalLoadingState.start()
const path = events[0]
if (path === '/') {
setIsInputBlanked(true)
setCurrentIssueUrl('')
return
}
const currentUrl = getValidUrlFromInput(path.split('#')[0].replace('/roadmap/', ''))
currentUrl.searchParams.delete('crumbs')
setCurrentIssueUrl(currentUrl.toString())
}
const currentUrl = getValidUrlFromInput(path.split('#')[0].replace('/roadmap/', ''));
currentUrl.searchParams.delete('crumbs');
setCurrentIssueUrl(currentUrl.toString());
});
Router.events.on('routeChangeStart', handleRouteChange)

return () => {
Router.events.off('routeChangeStart', handleRouteChange)
}
}, [globalLoadingState])

return (
isSmallScreen ?
<SearchIcon color='#FFFFFF' /> :
<form onSubmit={formSubmit}>
isSmallScreen
? <SearchIcon color='#FFFFFF' />
: <form onSubmit={formSubmit}>
<FormControl isInvalid={error != null} isDisabled={globalLoadingState.get()}>
<InputGroup>
<InputLeftElement
pointerEvents='none'
children={<SearchIcon color='#FFFFFF' />}
/>
<InputLeftElement pointerEvents='none'><SearchIcon color='#FFFFFF' /></InputLeftElement>
<Input
type="text"
value={currentIssueUrl}
Expand All @@ -128,10 +132,10 @@ export function RoadmapForm() {
borderColor={theme.light.header.input.border.color}
borderRadius={theme.light.header.input.border.radius}
/>
<InputRightElement cursor="pointer" children={inputRightElement} />
<InputRightElement cursor="pointer">{inputRightElement}</InputRightElement>
</InputGroup>
<FormErrorMessage>{error?.message}</FormErrorMessage>
</FormControl>
</form>
);
)
}
5 changes: 3 additions & 2 deletions components/RoadmapList/BulletConnector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import styles from './BulletConnector.module.css';
import React from 'react'

import styles from './BulletConnector.module.css'

/**
* The vertical line connecting each bulletIcon across all rows
Expand Down
1 change: 1 addition & 0 deletions components/RoadmapList/BulletIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CircularProgress } from '@chakra-ui/react'
import React from 'react'

import styles from './BulletIcon.module.css'

export default function BulletIcon ({ completion_rate }: {completion_rate: number}) {
Expand Down
36 changes: 18 additions & 18 deletions components/RoadmapList/RoadmapListItemDefault.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Grid, GridItem, Center, Link, HStack, Text, Skeleton } from '@chakra-ui/react';
import { LinkIcon } from '@chakra-ui/icons';
import NextLink from 'next/link';
import { useRouter } from 'next/router';
import React from 'react';
import { LinkIcon } from '@chakra-ui/icons'
import { Grid, GridItem, Center, Link, HStack, Text, Skeleton } from '@chakra-ui/react'
import NextLink from 'next/link'
import { useRouter } from 'next/router'
import React from 'react'

import SvgGitHubLogo from '../icons/svgr/SvgGitHubLogo';
import BulletConnector from './BulletConnector';
import BulletIcon from './BulletIcon';
import { paramsFromUrl } from '../../lib/paramsFromUrl';
import { dayjs } from '../../lib/client/dayjs';
import { getLinkForRoadmapChild } from '../../lib/client/getLinkForRoadmapChild';
import { ViewMode } from '../../lib/enums';
import { useGlobalLoadingState } from '../../hooks/useGlobalLoadingState';
import { ListIssueViewModel } from './types';
import { useGlobalLoadingState } from '../../hooks/useGlobalLoadingState'
import { dayjs } from '../../lib/client/dayjs'
import { getLinkForRoadmapChild } from '../../lib/client/getLinkForRoadmapChild'
import { ViewMode } from '../../lib/enums'
import { paramsFromUrl } from '../../lib/paramsFromUrl'
import SvgGitHubLogo from '../icons/svgr/SvgGitHubLogo'
import BulletConnector from './BulletConnector'
import BulletIcon from './BulletIcon'
import { ListIssueViewModel } from './types'

interface RoadmapListItemDefaultProps {
issue: ListIssueViewModel
Expand All @@ -22,7 +22,7 @@ interface RoadmapListItemDefaultProps {

function TitleText ({ hasChildren, issue }: Pick<RoadmapListItemDefaultProps, 'issue'> & {hasChildren: boolean}) {
return (
<Text fontWeight="semibold" color="linkBlue" fontSize={"xl"} lineHeight="32px">
<Text fontWeight="semibold" color="linkBlue" fontSize={'xl'} lineHeight="32px">
{hasChildren ? <LinkIcon lineHeight="32px" boxSize="10px" /> : null} {issue.title}
</Text>
)
Expand All @@ -45,7 +45,7 @@ function TitleTextMaybeLink ({ issue, hasChildren, index, childLink }: Pick<Road
export default function RoadmapListItemDefault ({ issue, index, issues }: RoadmapListItemDefaultProps) {
const { owner, repo, issue_number } = paramsFromUrl(issue.html_url)
const childLink = getLinkForRoadmapChild({ issueData: issue, query: useRouter().query, viewMode: ViewMode.List })
const globalLoadingState = useGlobalLoadingState();
const globalLoadingState = useGlobalLoadingState()
const hasChildren = childLink !== '#'
const issueDueDate = issue.due_date ? dayjs(issue.due_date).format('MMM D, YYYY') : 'unknown'

Expand Down Expand Up @@ -75,8 +75,8 @@ export default function RoadmapListItemDefault ({ issue, index, issues }: Roadma
<HStack gap={0} alignItems="flex-start">
<Link href={issue.html_url} lineHeight="32px" isExternal>
<Skeleton isLoaded={!globalLoadingState.get()}>
<HStack gap={0} alignItems="center" wrap={"nowrap"}>
<SvgGitHubLogo color="text" style={{ display:'inline', color: '#313239' }} fill="#313239" />
<HStack gap={0} alignItems="center" wrap={'nowrap'}>
<SvgGitHubLogo color="text" style={{ display: 'inline', color: '#313239' }} fill="#313239" />
<Text color="text" style={{ whiteSpace: 'nowrap' }} fontSize="large">{owner}/{repo}#{issue_number}</Text>
</HStack>
</Skeleton>
Expand Down
Loading