Skip to content

AkashaRojee/stock-screener

Repository files navigation

Stock Screener

Stock Screener SPA

screenshot

This project is a stock screener SPA built as my React capstone at Microverse. Users can view stock data for selected markets and access company quotes.

It is built with React and Redux, implements the Financial Modeling Prep API, and is tested with React Testing Library and Mock Service Worker.

Project Highlights: view video presentation

- Built with React's reusability principle in mind

<>
{ (loading && (<LoadingIndicator />)) || (
<>
<HighlightCard
image={symbols[0].symbol}
name={symbols[0].name}
metric={symbols[0].price}
/>
<SectionTitle title="STATS BY COMPANY" />
<div className={styles.symbolCards}>
{symbols.map(({ symbol, name, price }, index) => {
[counter, colour] = selectColour(index, counter, colour);
return (
<SymbolCard
colourClass={colour}
key={symbol}
image={symbol}
name={name}
metric={price}
/>
);
})}
</div>
</>
)}
</>

<>
{ (loading && (<LoadingIndicator />)) || (wantedSymbol && (
<>
<HighlightCard
image={symbol}
name={wantedSymbol.name}
metric={wantedSymbol.price}
/>
<SectionTitle title="QUOTE BREAKDOWN" filter={false} />
<div className={styles.dataRows}>
{Object.entries(wantedSymbol).map(([dataItem, dataValue]) => {
colour = !colour;
return (
<DataRow
colourClass={colour}
key={dataItem}
item={dataItem}
metric={(dataValue && dataValue.toString()) || 'N/A'}
/>
);
})}
</div>
</>
))}
</>

- Uses component composition via a component I custom-built to manage flexbox structures

<SplitPane
layout={new SplitPaneLayout('row', 'col', 'col', styles)}
first={
(
<div className={styles.image}>
<img
src={`https://financialmodelingprep.com/image-stock/${image}.jpg`}
alt={name}
aria-label="highlighted image"
/>
</div>
)
}
second={
(
<>
<div className={styles.title} aria-label="highlighted name">{ name.toUpperCase() }</div>
<div aria-label="highlighted price">
$
{ metric }
</div>
</>
)
}
/>

const SplitPane = ({ layout, first, second }) => (
<div className={`${layout.componentStyles.pane} ${styles[layout.pane]}`}>
<div className={`${layout.componentStyles.first} ${styles[layout.first]}`}>
{first}
</div>
<div className={`${layout.componentStyles.second} ${styles[layout.second]}`}>
{second}
</div>
</div>
);

export default class SplitPaneLayout {
constructor(pane, first, second, styles) {
this.pane = pane;
this.first = first;
this.second = second;
this.componentStyles = styles;
}
}

- Data fetching and loading from API is managed using thunk lifecycle actions via createAsyncThunk

export const getSymbolsList = createAsyncThunk('/stock/list/get', async () => {
const { data } = await axios.get('https://financialmodelingprep.com/api/v3/stock/list?apikey=94d238a41487fee05de69bf52aa5deec');
return data;
});
export const marketSlice = createSlice({
name: 'market',
initialState: { entities: [], selectedMarket: 'New York Stock Exchange', loading: true },
reducers: {
selectMarket: (state, action) => {
state.selectedMarket = action.payload;
},
},
extraReducers: (builder) => {
builder.addCase(getSymbolsList.pending, (state) => {
state.loading = true;
});
builder.addCase(getSymbolsList.fulfilled, (state, action) => {
state.loading = false;
action.payload.forEach(({
symbol, name, price, exchange,
}) => {
let marketObj = findObjInArr(state.entities, 'market', exchange);
if (marketObj) marketObj = marketObj.symbols.push({ symbol, name, price });
else {
state.entities.push({
market: exchange,
symbols: [{ symbol, name, price }],
});
}
});
});
},
});

- Testing API calls is made at the network level with proper mocking using Mock Service Worker. View my blog post about it.

- Customised React Testing Library's render method so that tests can access real Redux store and logic and use actual app behaviour

function render(
ui,
{
preloadedState,
store = configureStore(
{
reducer: { market: marketReducer, symbols: symbolsReducer },
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
serializableCheck: false,
}),
preloadedState,
},
),
...renderOptions
} = {},
) {
function Wrapper({ children }) {
return <Provider store={store}>{children}</Provider>;
}
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
}
export * from '@testing-library/react';
export { render };

Built With

  • Major languages: JS, SCSS
  • Others: JSX, Sass
  • Libraries used: React, Redux, Mock Service Worker

Live Demo

Please, view in mobile version: Live Demo Link

Getting Started

To get a local copy up and running, follow the steps below in your terminal.

Prerequisites

  • Node.js
  • npm

For more information, view the section Installation in this guide about Node.js and npm.

Setup

Clone the project:

git clone https://github.com/AkashaRojee/stock-screener.git

Install

Install dependencies:

npm install

Usage

The assets are in the src directory.

The output is in the build directory.

Project structure

The project is structured as feature folders with ducks slices.

- app //global setup and styling
- common //generic components
- features
  - /feature
    - Feature.js //main file of feature
    - featureSlice.js //feature's reducer
- mocks

Component hierarchy

- App
  - Header
  - Route: Home
    - HighlightCard
    - SectionTitle
    - SymbolCard(s)
  - Route: Symbols
    - HighlightCard
    - SectionTitle
    - DataRow(s)

Routing implementation

Home page

  • Upon initial render, get list of symbols from stock API endpoint, save into store, then display in page.
  • Clicking on a symbol opens its details page.

Details page

  • Upon initial render, get data about symbol from quote API endpoint, save into store, then display in page.
  • Clicking on back button returns to home page.

Deployment

Note: The content of the default output directory of React (build) is copied to docs upon build to facilitate publishing from GitHub Pages.

To build the website:

npm run build

To serve the website directly:

npm run start

Author

πŸ‘€ Akasha Rojee

🀝 Contributing

Contributions, issues, and feature requests are welcome!

Show your support

Give a ⭐️ if you like this project!

Acknowledgements

πŸ“ License

This project is MIT licensed.

About

View market stocks & company quotes. React-Redux SPA tested w/ RTL & MSW.

Topics

Resources

Stars

Watchers

Forks

Languages