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

develop #1

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
75bee68
feat: add basic components and types
falconx Sep 3, 2022
16474a2
feat: add localstorage support for global state
falconx Sep 3, 2022
89c4409
improve state management and allow for column titles to be modified
falconx Sep 3, 2022
25baa87
feat: allow cards to be added
falconx Sep 3, 2022
15e1859
feat: allow adding new columns
falconx Sep 3, 2022
50faa2b
fix: clear add new card fields on save
falconx Sep 3, 2022
b3a7d86
feat: allow removing columns
falconx Sep 3, 2022
3835b74
feat: minor style improvements
falconx Sep 3, 2022
fa61615
test: add tests for api
falconx Sep 3, 2022
6635f54
fix: prevent empty section name
falconx Sep 3, 2022
0bfd230
refactor: flatten state
falconx Sep 4, 2022
0f8696d
fix: add missing main landmark
falconx Sep 4, 2022
2f4b1a0
fix: modal closing when click is fired within the content area
falconx Sep 4, 2022
a3856bc
fix: modal remounting
falconx Sep 4, 2022
ece20fc
feat: support card description
falconx Sep 4, 2022
28db557
feat: show clamped card description
falconx Sep 4, 2022
22c89c8
feat: allow dragging cards between columns
falconx Sep 4, 2022
566ed93
feat: improve styles
falconx Sep 5, 2022
0d25f41
refactor: remove references to weight
falconx Sep 5, 2022
e01ab53
feat: add default board state to demonstrate example usage
falconx Sep 5, 2022
8378531
feat: introduce Input component
falconx Sep 5, 2022
34d1d4e
fix: document title
falconx Sep 5, 2022
01fd865
test: add more test cases
falconx Sep 5, 2022
2aecdf7
docs: update README.md
falconx Sep 5, 2022
b519e9d
fix: column should not be draggable
falconx Sep 5, 2022
5cc903a
docs: update typo
falconx Sep 5, 2022
602e4b5
fix: drag and drop behaviour
falconx Sep 5, 2022
57b8822
docs: update README
falconx Sep 6, 2022
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
66 changes: 34 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,48 @@
# Getting Started with Create React App

This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).

## Available Scripts

In the project directory, you can run:

### `npm start`

Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.

The page will reload if you make edits.\
You will also see any lint errors in the console.

### `npm test`

Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
# Quick start

### `npm run build`
```bash
yarn # Install dependencies
yarn start # Run the app in dev mode
```

Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
# Deployments

The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
Pushing to the `develop` branch will trigger a deploy to https://trello-nine.vercel.app

See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
# What else you would've added to the project if this was going to be released as a real app?

### `npm run eject`
## Features

**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
* Add an image field for each card to allows users to specify an image to show in the edit modal and card preview
* Add routing support for cards so that each edit modal view is mounted at `/:card-id`
* Update document title with the card title to improve SEO
* Form validation with error messages
* Enhance reordering
* Allow for cards to be reordered within a column
* Allow for columns to be reordered
* Add touch support
* In a real app I would make use of a well-established library for managing the drag and drop behaviour
* User accounts and authentication
* Assign users to cards
* Checklists
* Search
* Images
* Deadline dates

If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
## Accessibility improvements

Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
* It's currently not possible to reorder cards using only a keyboard — To solve this we could have a dropdown field within the edit modal view which lists all the available column titles and, on selection, reassigns the card `columnId`
* There are lots of a11y considerations for modals, the easiest way to meet these standards is by using [react-modal](https://github.com/reactjs/react-modal)
* It is currently not possible to hide the add card and add list fields using only a keyboard
* Hover/focus styles

You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
## Issues

## Learn More
1. Prevent body scrolling on when modal is present — Mostly an issue on mobile
1. `activeDragCardId` state will unnecessarily be stored in localStorage (See `AppContext.tsx`)

You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
Not a big deal now but could bloat our local storage when adding more features which require temporary global state.

To learn React, check out the [React documentation](https://reactjs.org/).
We could have a separate typedef for stored global state and have `activeDragCard` use `useState` instead of `useLocalStorage`.
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"typescript": "^4.8.2",
"uuid": "8.3.2",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand All @@ -39,5 +40,10 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/lodash": "4.14.184",
"@types/uuid": "8.3.4",
"lodash": "4.17.21"
}
}
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Trello</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
38 changes: 0 additions & 38 deletions src/App.css

This file was deleted.

44 changes: 44 additions & 0 deletions src/App.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
:root {
--shadow: 0 1px 2px -1px #091e4240, 0 0 0 1px #091e4214;

/* spacing scale */
--space-1: 0.375rem;
--space-2: 0.5rem;
--space-3: 0.625rem;
--space-4: 0.75rem;
--space-5: 0.875rem;
--space-6: 1rem;
--space-7: 1.125rem;

/* type scale */
--text-small: 0.875rem;

/* generic colors */
--c-white: #fff;
--c-black: #222;
--c-grey: #ebecf0;
--c-light-grey: #5e6c84;
--c-blue: #0079bf;
--c-light-blue: #e2f4ff;
--c-red: #d1576e;
--c-light-red: #ffecec;

/* component colors */
--c-overlay: #000000a3;
--c-card: var(--c-white);
--c-column: var(--c-grey);
--c-column-drag-highlight: var(--c-light-blue);

--c-button-primary: var(--c-blue);
--c-button-primary-text: var(--c-white);
--c-button-secondary: var(--c-light-blue);
--c-button-secondary-text: var(--c-blue);
--c-button-remove: var(--c-light-red);
--c-button-remove-text: var(--c-red);
--c-button-disabled: var(--c-grey);
--c-button-disabled-text: var(--c-black);
}

body {
font-family: Arial, Helvetica, sans-serif;
}
27 changes: 23 additions & 4 deletions src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import React from 'react';
import { render, screen } from '@testing-library/react';

import App from './App';

test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
describe('App', () => {
it('renders page header', async () => {
render(<App />);

const heading = await screen.findByRole('heading', { level: 1 });
expect(heading).toBeInTheDocument();
});

it('renders default column headers', async () => {
render(<App />);

let column;

column = await screen.findByText('To Do');
expect(column).toBeInTheDocument();

column = await screen.findByText('In Progress');
expect(column).toBeInTheDocument();

column = await screen.findByText('Done');
expect(column).toBeInTheDocument();
Comment on lines +17 to +26
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I wrote this as:

const headers = ['To Do', 'In Progress', 'Done'];

await Promise.all(headers.map(async (text) => {
  const column = await screen.findByText(text);
  expect(column).toBeInTheDocument();
}));

however, apart from being more difficult to read, a test failure would only report that the const column = await screen.findByText(text); line failed, and not which of the scenarios was to blame.

});
});
36 changes: 14 additions & 22 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
import { AppProvider } from './AppContext';
import Board from './components/Board';

import './App.module.css';

const App: React.FunctionComponent = () => (
<AppProvider>
<main>
<Board />
</main>

<div id="modal-root" />
</AppProvider>
);

export default App;
12 changes: 12 additions & 0 deletions src/AppContext.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';

describe('AppContext', () => {
describe('stores app state in localStorage', () => {
it.todo('addCard');
it.todo('removeCard');
it.todo('updateCard');
it.todo('addColumn');
it.todo('removeColumn');
it.todo('updateColumn');
});
});
111 changes: 111 additions & 0 deletions src/AppContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React, { createContext } from 'react';

import { GlobalState } from './types/GlobalState';
import { Column, EditableColumnFields } from './types/Column';
import { Card, EditableCardFields } from './types/Card';

import * as api from './api';
import useLocalStorage from './hooks/useLocalStorage';
import { LOCAL_STORAGE_KEY } from './constants';

export interface GlobalAppContext {
state: GlobalState;
dragCard: (cardId: string) => void;
addCard: (value: Card) => void;
removeCard: (cardId: string) => void;
updateCard: (cardId: string, value: EditableCardFields) => void;
addColumn: (value: Column) => void;
removeColumn: (columnId: string) => void;
updateColumn: (columnId: string, value: EditableColumnFields) => void;
}

const defaultState: GlobalState = {
columns: [
{
id: 'todo',
title: 'To Do',
},
{
id: 'in-progress',
title: 'In Progress',
},
{
id: 'done',
title: 'Done',
},
],
cards: [
{
id: 'card-0',
columnId: 'done',
title: 'Make breakfast',
description: `In order to make breakfast you'll need the following ingredients:\n\n* Eggs\n* Sausages\n* Beans\n* Hash browns\n* Toast`,
},
{
id: 'card-1',
columnId: 'todo',
title: 'Check mail',
},
{
id: 'card-2',
columnId: 'todo',
title: 'Get to work',
},
],
};
Comment on lines +22 to +55
Copy link
Owner Author

@falconx falconx Sep 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that it's easy to clear the board, I figured it was nice to show the user a default example usage.


const noop = () => {};

const AppContext = createContext<GlobalAppContext>({
state: defaultState,
dragCard: noop,
addCard: noop,
removeCard: noop,
updateCard: noop,
addColumn: noop,
removeColumn: noop,
updateColumn: noop,
});

const AppConsumer = AppContext.Consumer;

interface Props {
children?: React.ReactNode;
}

const AppProvider: React.FunctionComponent<Props> = props => {
const [state, setState] = useLocalStorage<GlobalState>(LOCAL_STORAGE_KEY, defaultState);

// TODO: opted for conciseness here but type safety could be improved
// by ensuring the context and api function signatures are matched
const update = (fn: Function, ...args: any) => setState(fn(state, ...args));

const dragCard = (cardId: string) => {
setState({
...state,
activeDragCardId: cardId,
});
};

return (
<AppContext.Provider
value={{
state,
addCard: update.bind(null, api.addCard),
removeCard: update.bind(null, api.removeCard),
updateCard: update.bind(null, api.updateCard),
addColumn: update.bind(null, api.addColumn),
removeColumn: update.bind(null, api.removeColumn),
updateColumn: update.bind(null, api.updateColumn),
dragCard,
}}
{...props}
/>
)
};

export {
AppContext,
AppProvider,
AppConsumer,
};
Loading