This is a solution to the Job listings with filtering challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.
This is the front-end mentor's eighteen challenge. The challenge is to build the "Job listings with filtering" and make it as close to the design as possible. Building the desing with whatever you want to finish, any language, framework or tools.
Users should be able to:
- View the optimal layout for the site depending on their device's screen size
- See hover states for all interactive elements on the page
- Filter job listings based on the categories
-
Solution URL: My solution for this challenge
-
Live Site URL: check the result
-
My figma design: Figma
- Mobile-first workflow
- typeScript
- React - JS library
- Styled components - CSS in js with stiled components
Abaixo há a forma de adicionar novas tags ao array de filtros. Será adicionado os filtros já existentes e o novo filtro. Há uma validação antes, para saber se o filtro já existe dentro do array. Isso é feito com o método "some" que retorna true se pelo menos um dos elementos do array passar na condição, que nesse caso é "o elemento do array é igual a palavra que está para ser adicionada no array?", onde o elemento do array é chamado de data e a palavra é o result, pois é o resultado após o tratamento para chegar no valor em questão após o click no botão da tag.
Para pegar o "value" de um elemento "button" foi feito com que se entenda o "button" como um "input" e também se passou um "value" como uma propriedade do mesmo.
To get the "value" of a "button" element, the "button" was understood as an "input" and also passed a "value" as a property of it.
const onclick = (e: React.MouseEvent<HTMLButtonElement>) => {
const result = (e.target as HTMLInputElement).value;
const canAdd = currentFilters.some((data) => data === result);
if (!canAdd) {
const resultString = [...currentFilters, result];
setFilters(resultString);
}
};
<S.filterTag
value={data.role}
onClick={(e: React.MouseEvent<HTMLButtonElement>) => onclick(e)}
>
{data.role}
</S.filterTag>;
O "useState" deve ser tipado quanto aos valores que irá receber. Então quando chamamos o componente passando os parâmetros necessários devemos também tipar os mesmo no componente.
const [filters, setFilters] = useState<string[]>([]);
<SearchJob currentFilters={filters} setFilters={setFilters} />;
Essa tipagem foi feita da forma de uma 'interface'. Sendo que elas são compatíveis com o tipo aderido ao 'useState'.
interface SearchJobInterface {
currentFilters: string[];
setFilters: React.Dispatch<React.SetStateAction<string[]>>;
}
export const SearchJob = ({
currentFilters,
setFilters,
}: SearchJobInterface): JSX.Element => {};
A mostra ou não dos empregos é feita por uma flag de controle chamada "canShow", no useState é inserido inicialmente todos como valor "true", então todos são mostrados. O "useEffect" remonta o componente todas as vezes que os filtros mudarem. Então atribui todos os filtros a uma constante. E cria outra constante com quais dados há nos empregos disponíveis, relevando apenas os elementos que podem ser afetados pelos filtros. Então é feito a comparação, "para cara filtro existente em filtersTexts cheque se ele está incluido nos textsToCompare", isso faz com que dentro de cada iteração do map percorrendo o allJobs se crie um textsToCompare com os dados do que aquele emprego tem, compare com as tags de filtro em filterText e se todas estiverem inclusas é um caso 'true' se não é um caso 'false'. Cada um retorna a flag de controle corresponde, preenchendo o array updatedJobs com os empregos que devem ser atualizados. Que são atribuidos ao allJobs no final.
const [allJobs, setAllJobs] = useState(
data.map((job) => ({ ...job, canShow: true })),
);
useEffect(() => {
const filterTexts = filters.map((filter) => filter);
const updatedJobs = allJobs.map((job) => {
const textsToCompare = [
job.role,
job.level,
...job.languages,
...job.tools,
job.new && 'New',
job.featured && 'Featured',
];
if (
filterTexts.every((filterText) => textsToCompare.includes(filterText))
) {
return { ...job, canShow: true };
} else {
return { ...job, canShow: false };
}
});
setAllJobs(updatedJobs);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filters]);
Para adicionar New e Featured foram feitas funções separadas, que seguem a mesma lógica, mas passando uma string para ser comparada.
const handleAdd = (tag: string) => {
const canAdd = currentFilters.some((data) => data === tag);
if (!canAdd) {
const resultString = [...currentFilters, tag];
setFilters(resultString);
}
};
<div className="new" onClick={() => handleAdd('New')}>
<h2>New!</h2>
</div>
<div className="featured" onClick={() => handleAdd('Featured')}>
<h2>Featured</h2>
</div>
- react tutorial - This helped me structure the components and build the proposed page.
- my figma design - My figma design for help anyone who wants to build this challenge.
- CSS units conversor - px to VH/VW/REM - CSS units conversor .
- Converting Colors - HSL for all color systems.
- Personal Page - Jean Carlos De Meira
- Frontend Mentor - @JCDMeira
- Instagram - @jean.meira10
- GitHub - JCDMeira