Skip to content
This repository has been archived by the owner on Dec 18, 2024. It is now read-only.

Filter resources by topic #32

Closed
wants to merge 64 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
bd3e4c5
add topic filter
alinosratipour Jan 16, 2024
ba725b7
Tweak
alinosratipour Jan 17, 2024
127dd63
add className prop to select
alinosratipour Jan 17, 2024
1ce9c43
filter topic in all pages
alinosratipour Jan 18, 2024
7d622f2
Merge branch 'main' into FilterTopic
alinosratipour Jan 18, 2024
b5ea4b4
tweak
alinosratipour Jan 18, 2024
4d54098
format
alinosratipour Jan 18, 2024
3ab2919
Tweak
alinosratipour Jan 19, 2024
e61bcc4
changed test
alinosratipour Jan 19, 2024
9757d3c
removed unwanted changes
alinosratipour Jan 19, 2024
916d4a9
draft commit
alinosratipour Jan 20, 2024
2b2255f
fix test
alinosratipour Jan 22, 2024
edc2096
sort code format
alinosratipour Jan 22, 2024
b3d28ac
refactor
alinosratipour Jan 22, 2024
5f36e6d
add error handling
alinosratipour Jan 22, 2024
71fbf4f
Tweak
alinosratipour Jan 22, 2024
d39435e
revert change
alinosratipour Jan 23, 2024
821ec98
fix console error
alinosratipour Jan 23, 2024
b356b32
change in the way we handle topics
alinosratipour Jan 23, 2024
63ca74e
draft
alinosratipour Jan 26, 2024
aa2f9fb
removed unused code
alinosratipour Jan 26, 2024
23b73e1
pushed this to see how i will work in CI
alinosratipour Jan 27, 2024
92ab284
add settopcics
alinosratipour Jan 27, 2024
42e0923
address pr commnets
alinosratipour Feb 21, 2024
90d78d3
change css
alinosratipour Feb 22, 2024
6e1e63a
addressed pr
alinosratipour Feb 22, 2024
f725682
refactor
alinosratipour Feb 23, 2024
9a4e54b
remove commnet
alinosratipour Feb 23, 2024
5b02254
tweak
alinosratipour Feb 23, 2024
fb897da
tweak
alinosratipour Feb 24, 2024
6cd4325
wrote unit test for new feature filter resouce by topics
alinosratipour Feb 27, 2024
002b989
removed unused prop
alinosratipour Feb 29, 2024
f37172b
removed unused prop
alinosratipour Feb 29, 2024
77c0942
created a new component for the draft list
alinosratipour Feb 29, 2024
a2cf306
add new query to fetch all resources
alinosratipour Mar 4, 2024
23e6a69
renamed variable
alinosratipour Mar 4, 2024
7d5d0c9
refactored finction
alinosratipour Mar 4, 2024
ea0097c
made filter resource correspond with the number of filtered resources
alinosratipour Mar 5, 2024
c363fce
Tweak
alinosratipour Mar 5, 2024
f7ce556
add new hooks
alinosratipour Mar 6, 2024
94167bf
refacor code add some more hooks
alinosratipour Mar 6, 2024
73f1253
refactor hooks
alinosratipour Mar 7, 2024
20d072f
updated resourceList test
alinosratipour Mar 7, 2024
e7c20ca
add test displays only resources with the selected topic
alinosratipour Mar 7, 2024
42cbfc5
made small change to resource list to handle undifend
alinosratipour Mar 7, 2024
b02a0b5
removed unusfule commnets
alinosratipour Mar 7, 2024
3e73063
add new test to display resource
alinosratipour Mar 8, 2024
4d20a7c
removed Home test since there are no more logic exist in there
alinosratipour Mar 8, 2024
b838cdd
changed the way we query allResource
alinosratipour Mar 11, 2024
72f36f0
recover Home test
alinosratipour Mar 11, 2024
9a713fd
tweak
alinosratipour Mar 12, 2024
15d70cb
add e2e test for filtering resource by topic
alinosratipour Mar 14, 2024
e57c2d8
refactor some tests in suggest and topicservice
alinosratipour Mar 18, 2024
18e41c1
moved filter to server side
alinosratipour Mar 19, 2024
36a4e0a
tweak
alinosratipour Mar 19, 2024
5779266
fixed pagination
alinosratipour Mar 19, 2024
4207fe1
tweak
alinosratipour Mar 19, 2024
370d20f
removed comment
alinosratipour Mar 19, 2024
ea9fda5
ad usenavigate hook
alinosratipour Mar 19, 2024
73a1bf2
fix pagination display from server
alinosratipour Mar 20, 2024
20a7d9a
tweak
alinosratipour Mar 20, 2024
91853e0
removed comments
alinosratipour Mar 20, 2024
5859f54
fix test
alinosratipour Mar 20, 2024
81003a2
refactor test
alinosratipour Mar 20, 2024
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
Prev Previous commit
Next Next commit
created a new component for the draft list
alinosratipour committed Feb 29, 2024
commit 77c0942d149deffe9853725365cc9550be2fada4
48 changes: 1 addition & 47 deletions client/src/components/ResourceList/ResourceList.test.js
Original file line number Diff line number Diff line change
@@ -7,57 +7,11 @@ import { resourceStub, server } from "../../../setupTests";
import ResourceList from "./index";

describe("ResourceList", () => {
it("shows the resource", () => {
const resource = resourceStub({
description:
'Comprehensive guide to setting up the various types of inputs with React (a.k.a. "data binding")',
title: "Data Binding in React",
url: "https://www.joshwcomeau.com/react/data-binding/",
});
// const mytopics = resourceStub({ topic_name: "HTML/CSS", topic: "45678r448666" });

render(<ResourceList resources={[resource]} />);
expect(screen.getByRole("heading", { level: 3 })).toHaveTextContent(
resource.title
);
expect(
screen.getByRole("link", { name: "joshwcomeau.com" })
).toHaveAttribute("href", resource.url);
expect(screen.getByText(/comprehensive guide/i)).toBeInTheDocument();
});

it("shows a message if no resources are available", () => {
render(<ResourceList resources={[]} />);
expect(screen.getByText(/no resources to show/i)).toBeInTheDocument();
});

it("shows a publish button if enabled", async () => {
const publish = jest.fn();
const resource = resourceStub();
const user = userEvent.setup();
server.use(
rest.get("/api/topics", (req, res, ctx) => {
const mockTopics = [
{ id: "1", name: "Topic 1" },
{ id: "2", name: "Topic 2" },
];
return res(ctx.json(mockTopics));
})
);

render(
<ResourceList
publish={publish}
resources={[resource]}
allowTopicFiltering={true}
/>
);

await user.click(screen.getByRole("button", { name: /publish/i }));

expect(publish).toHaveBeenCalledWith(resource.id);
});

it("shows the topic if available", () => {
const resource = resourceStub({ topic_name: "My Topic" });
render(<ResourceList resources={[resource]} />);
@@ -98,7 +52,7 @@ describe("ResourceList", () => {
)
);

render(<ResourceList resources={resources} allowTopicFiltering={true} />);
render(<ResourceList resources={resources} />);

// Wait for the select menu to be enabled and options to be populated
await waitFor(() => {
45 changes: 14 additions & 31 deletions client/src/components/ResourceList/index.js
Original file line number Diff line number Diff line change
@@ -3,13 +3,10 @@ import { useEffect, useState, useMemo } from "react";

import { FormControls } from "../../components";
import { TopicService, useService } from "../../services";
import { formatUrl } from "../../utils/utils";
import "./ResourceList.scss";

export default function ResourceList({
publish,
resources,
allowTopicFiltering,
}) {
export default function ResourceList({ resources }) {
const [selectedTopic, setSelectedTopic] = useState(undefined);
const [topics, setTopics] = useState([]);
const topicService = useService(TopicService);
@@ -40,18 +37,17 @@ export default function ResourceList({

return (
<>
{allowTopicFiltering && (
<div>
<FormControls.Select
label="Filter Topic"
placeholder="Select a topic"
name="filter topic"
options={topics}
onChange={handleChange}
className="custom-select"
/>
</div>
)}
<div>
<FormControls.Select
label="Filter Topic"
placeholder="Select a topic"
name="filter topic"
options={topics}
onChange={handleChange}
className="custom-select"
/>
</div>

<ul className="resource-list">
{displayResources.length === 0 && (
<li className="no-resources">
@@ -65,10 +61,7 @@ export default function ResourceList({
{topic_name && <span className="topic">{topic_name}</span>}
</div>
{description && <p>{description}</p>}
<div>
<a href={url}>{formatUrl(url)}</a>
{publish && <button onClick={() => publish(id)}>Publish</button>}
</div>
<a href={url}>{formatUrl(url)}</a>
</li>
))}
</ul>
@@ -77,7 +70,6 @@ export default function ResourceList({
}

ResourceList.propTypes = {
publish: PropTypes.func,
resources: PropTypes.arrayOf(
PropTypes.shape({
description: PropTypes.string,
@@ -88,13 +80,4 @@ ResourceList.propTypes = {
topic: PropTypes.string,
})
).isRequired,
allowTopicFiltering: PropTypes.bool,
};

function formatUrl(url) {
const host = new URL(url).host;
if (host.startsWith("www.")) {
return host.slice(4);
}
return host;
}
41 changes: 41 additions & 0 deletions client/src/pages/Drafts/DraftList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import PropTypes from "prop-types";

import "../../components/ResourceList/ResourceList.scss";
import { formatUrl } from "../../utils/utils";
export default function DraftList({ publish, resources }) {
return (
<ul className="resource-list">
{resources.length === 0 && (
<li className="no-resources">
<em>No resources to show.</em>
</li>
)}
{resources.map(({ description, id, title, topic_name, url }) => (
<li key={id}>
<div>
<h3>{title}</h3>
{topic_name && <span className="topic">{topic_name}</span>}
</div>
{description && <p>{description}</p>}
<div>
<a href={url}>{formatUrl(url)}</a>
{publish && <button onClick={() => publish(id)}>Publish</button>}
</div>
</li>
))}
</ul>
);
}

DraftList.propTypes = {
publish: PropTypes.func,
resources: PropTypes.arrayOf(
PropTypes.shape({
description: PropTypes.string,
id: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
topic_name: PropTypes.string,
url: PropTypes.string.isRequired,
})
).isRequired,
};
42 changes: 42 additions & 0 deletions client/src/pages/Drafts/Drafts.test.js
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@ import { rest } from "msw";

import { resourceStub, server } from "../../../setupTests";

import DraftList from "./DraftList";

import Drafts from "./index";

describe("Drafts", () => {
@@ -33,6 +35,46 @@ describe("Drafts", () => {
).resolves.toHaveTextContent("foo");
});

it("shows the resource", () => {
alinosratipour marked this conversation as resolved.
Show resolved Hide resolved
const publish = jest.fn();
const resource = resourceStub({
description:
'Comprehensive guide to setting up the various types of inputs with React (a.k.a. "data binding")',
title: "Data Binding in React",
url: "https://www.joshwcomeau.com/react/data-binding/",
});

render(<DraftList publish={publish} resources={[resource]} />);
expect(screen.getByRole("heading", { level: 3 })).toHaveTextContent(
resource.title
);
expect(
screen.getByRole("link", { name: "joshwcomeau.com" })
).toHaveAttribute("href", resource.url);
expect(screen.getByText(/comprehensive guide/i)).toBeInTheDocument();
});

it("shows a publish button if enabled", async () => {
const publish = jest.fn();
const resource = resourceStub();
const user = userEvent.setup();
server.use(
rest.get("/api/topics", (req, res, ctx) => {
const mockTopics = [
{ id: "1", name: "Topic 1" },
{ id: "2", name: "Topic 2" },
];
return res(ctx.json(mockTopics));
})
);

render(<DraftList publish={publish} resources={[resource]} />);

await user.click(screen.getByRole("button", { name: /publish/i }));

expect(publish).toHaveBeenCalledWith(resource.id);
});

it("lets those resources be published", async () => {
let patchRequest;
const resource = resourceStub({
8 changes: 6 additions & 2 deletions client/src/pages/Drafts/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useCallback, useEffect, useState } from "react";

import { ResourceList } from "../../components";
// import { ResourceList } from "../../components";
import { ResourceService, useService } from "../../services";

import DraftList from "./DraftList";

export default function Drafts() {
const [drafts, setDrafts] = useState([]);
const resourceService = useService(ResourceService);
@@ -28,7 +30,9 @@ export default function Drafts() {
<h2>Drafts</h2>
<p>Review resources that have been submitted but not yet published.</p>
<section>
<ResourceList publish={publish} resources={drafts} />
{/* <ResourceList publish={publish} resources={drafts} /> */}

<DraftList publish={publish} resources={drafts} />
</section>
</>
);
7 changes: 2 additions & 5 deletions client/src/pages/Home/index.js
Original file line number Diff line number Diff line change
@@ -8,17 +8,14 @@ export function Home() {
const resourceService = useService(ResourceService);
const searchParams = useSearchParams();
const [{ lastPage, resources } = {}, setEnvelope] = useState();
const allowTopicFiltering = true;

useEffect(() => {
resourceService.getPublished(searchParams).then(setEnvelope);
}, [resourceService, searchParams]);

return (
<section>
<ResourceList
resources={resources ?? []}
allowTopicFiltering={allowTopicFiltering}
/>
<ResourceList resources={resources ?? []} />
<Pagination lastPage={lastPage ?? 1} />
</section>
);
8 changes: 8 additions & 0 deletions client/src/utils/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Shared function to format URL
alinosratipour marked this conversation as resolved.
Show resolved Hide resolved
export function formatUrl(url) {
const host = new URL(url).host;
if (host.startsWith("www.")) {
return host.slice(4);
}
return host;
}