Thank you for your interest in contributing to the AirSwap Marketplace! We welcome contributions no matter their size.
While we use GitHub for issue tracking and project management, development is generally coordinated on the Discord server, which you should join to learn more about how and where to contribute.
- Craco (Create React App Configuration Override)
- AirSwap protocols
- ethers
- Redux
When multiple people are working on the same body of code, it is important that everyone conforms to a style. We use eslint for formatting our code.
We use BEM for styling. When styling a component put your scss in a separate .scss file. Please follow the rules lined out by sparkbox. When your component gets too crowded try to split your code in subcomponents.
We define dimensions using rem
based on 16px, so use units like 0.125rem
, 0.25rem
, etc.
The order of css properties should be based on matter of importance it has on the box-model. For instance these properties are sorted by their importance: display
, position
, margin
, border
, width
, padding
, line-height
, font-size
, z-index
, background
. This could be a little arbitrary so we're not very too about this.
On top you have @extend and @include. Then the component styles. After that the modifiers and then the children styles.
@import "src/styles/index";
.some-component {
@extend %flex-align-center;
border: 1px solid var(--c-light-grey);
width: 100%;
height: 3rem;
padding: 0.5rem;
background: var(--c-grey);
z-index: 1;
&--is-primary {
background: var(--c-red);
}
&__a-button,
&__b-button {
height: 2rem;
aspect-ratio: 1;
color: var(--c-white);
}
&__a-button {
position: absolute;
left: calc(50% - 1rem);
}
When naming of modifiers the same rules apply for react properties lined out in this article of David Linau. For example: --is-primary
and --has-header
.
When positioning an element using absolute
, fixed
or similar, the parent should set the style of the child.
Directly style tags (div
, span
, etc). Unless your branch is WIP and you add a TODO
above to it.
When making a new component please take a look at the current components to get an idea what style we are using. Here's an example of the interface of a simple component:
interface CheckboxProps {
hideLabel?: boolean;
isChecked: boolean;
label: string;
subLabel?: string;
onChange: (isChecked: boolean) => void;
className?: string;
}
For naming conventions please refer to this article of David Linau.
The order of properties are as following: first we have modifiers (ie: isActive
, hideLabel
), then other data properties (ie: items
, label
), then event handlers (ie: onClick
, onChange
) and finally the className?
which every components needs to have so it can be styled by it's parent.
As you can see below the order of data and handlers are: hooks, states, useEffect, event handlers.
const TopBar: FC<TopBarProps> = ({ hideLabel, className = '' }) => {
const { t } = useTranslation();
const [isConnecting, setIsConnecting] = useState(false);
const [mobileMenuIsVisible, setMobileMenuIsVisible] = useState(false);
useEffect(() => {
// Some effect here
}, [mobileMenuIsVisible]);
const handleMobileToggleButtonClick = () => {
setMobileMenuIsVisible(!mobileMenuIsVisible);
}
return (
<div className={`top-bar ${className}`}>
If you have dynamic modifiers for your BEM classes use classNames
preferable at the top of your component.
const checkboxClassNames = classNames('checkbox', {
'checkbox--is-checked': checked,
}, className);
We divide our components very similar to Atomic design from Brad Frost. We use 4 types of components:
- Component
- Composition
- Widget
- Page
The component
is the smallest. Then when a component has one or more components it's a composition
. A widget
is where all components are gathered and connected to the store and actions dispatched.
A page
is connected to the routing of the app and should direct the user to one or more widgets.
We use redux toolkit for store management. Use createAsyncThunk for all asynchronous actions. Errors should be transformed to AppError
, processed in the action and saved in the store. An example:
const UsersComponent = () => {
const { user, isLoading, error } = useSelector((state) => state.users)
const dispatch = useDispatch()
const fetchUser = (userId) => {
dispatch(fetchUserById(userId));
}
// render UI here
}
// TODO: some more explanation about error handling
New translations should be added manually to public/locales/en/translation.json
first. After your PR is merged an admin will add the new translations in POEditor. Everything in public/locales
will eventually be overwritten by POEditor. If you want to help with translating please let us know.
It’s a good idea to make PRs early on. A PR represents the start of a discussion, and doesn’t necessarily need to be the final, completed submission. Create a draft PR if you're looking for feedback but not ready for a final review. If the PR is in response to a GitHub issue, make sure to notate the issue as well.
Usually your PR is connected to a ticket number, so please put the ticket number (for example 101) in the description of your PR like so:
Fixes #101
GitHub’s documentation for working on PRs is available here.
Once your PR is ready, ensure all checks are passing and request a review.