Skip to content

Commit 02d3302

Browse files
authored
Merge pull request creativecommons#502 from creativecommons/issue_finder
Create an Issue Finder page on the site
2 parents ca6da21 + c953058 commit 02d3302

File tree

14 files changed

+389
-18
lines changed

14 files changed

+389
-18
lines changed

content/contributing-code/contents.lr

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ We make extensive use of issue labels to designate the priority, status and begi
3434
- **Issues without any of the above labels:**
3535
- These issues _may_ (or may not) be open for contribution.
3636
- Please add a comment asking one of the maintainers to triage the issue and label it as appropriate.
37+
38+
You can use our [Issue Finder tool](/contributing-code/issue-finder/) to find a good issue that matches your skills and familiarity with our software and community.
3739

3840
Some helpful saved searches on GitHub than can assist with finding an issue:
3941
- [issues labeled "good first issue"](https://github.com/search?q=org%3Acreativecommons+is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+-linked%3Apr)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
_model: page
2+
---
3+
_template: issue_finder.html
4+
---
5+
title: Issue Finder
6+
---
7+
description: Welcome to the CC developer community! We're absolutely delighted to have you here. If you want to contribute but are unsure where to start, you can use this issue finder to find an issue that matches your skills and experience level.
8+
---
9+
body:
10+
11+
Welcome to the CC developer community!

content/contributing-code/repo-labels/contents.lr

+11-11
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,19 @@ The priority of an issue is based on its impact, derived from a combination of
6363
urgency and importance. This determines the importance of the issue when sprint
6464
planning or deciding which issues to tackle next.
6565

66-
- <span class="gh-label has-text-white" style="background-color: #b60205;">
66+
- <span class="gh-label priority-unfavourable">
6767
🟥 priority: critical
6868
</span>
6969
- **Description:** Must be fixed ASAP
70-
- <span class="gh-label has-text-black" style="background-color: #ff9f1c;">
70+
- <span class="gh-label priority-negative">
7171
🟧 priority: high
7272
</span>
7373
- **Description:** Stalls work on the project or its dependents
74-
- <span class="gh-label has-text-black" style="background-color: #ffcc00;">
74+
- <span class="gh-label priority-neutral">
7575
🟨 priority: medium
7676
</span>
7777
- **Description:** Not blocking but should be fixed soon
78-
- <span class="gh-label has-text-black" style="background-color: #cfda2c;">
78+
- <span class="gh-label priority-positive">
7979
🟩 priority: low
8080
</span>
8181
- **Description:** Low priority and doesn't need to be rushed
@@ -113,32 +113,32 @@ may not be ready to be worked on for a number of reasons and the maintainers
113113
must keep updating the labels as the situation evolves.
114114

115115
An issue, at the time of closing can have either the
116-
<span class="gh-label has-text-black" style="background-color: #cccccc;">
116+
<span class="gh-label status-light">
117117
🏁 status: ready for dev
118118
</span>
119119
or the
120-
<span class="gh-label has-text-black" style="background-color: #eeeeee;">
120+
<span class="gh-label status-lighter">
121121
⛔️ status: discarded
122122
</span>
123123
label based on whether it was closed with or without resolution, respectively.
124124

125-
- <span class="gh-label has-text-black" style="background-color: #cccccc;">
125+
- <span class="gh-label status-light">
126126
🏁 status: ready for dev
127127
</span>
128128
- **Description:** Ready for work
129-
- <span class="gh-label has-text-black" style="background-color: #999999;">
129+
- <span class="gh-label status-neutral">
130130
🚧 status: blocked
131131
</span>
132132
- **Description:** Blocked & therefore, not ready for work
133-
- <span class="gh-label has-text-white" style="background-color: #666666;">
133+
- <span class="gh-label status-dark">
134134
🧹 status: ticket work required
135135
</span>
136136
- **Description:** Needs more details before it can be worked on
137-
- <span class="gh-label has-text-black" style="background-color: #eeeeee;">
137+
- <span class="gh-label status-lighter">
138138
⛔️ status: discarded
139139
</span>
140140
- **Description:** Will not be worked on
141-
- <span class="gh-label has-text-white" style="background-color: #333333;">
141+
- <span class="gh-label status-darker">
142142
🚦 status: awaiting triage
143143
</span>
144144
- **Description:** Has not been triaged & therefore, not ready for work
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{% extends "layout.html" %}
2+
3+
{% block title %}{{ this.title }}{% endblock %}
4+
5+
{% block body %}
6+
<div class="issue-finder">
7+
<div class="header">
8+
<div class="container is-paddingless">
9+
<h1>{{ this.title }}</h1>
10+
<div class="description column is-9 is-paddingless">
11+
{{ this.description }}
12+
</div>
13+
</div>
14+
</div>
15+
<div class="body container">
16+
<div id="vue-app">
17+
{{ this.body }}
18+
<noscript>
19+
The issue finder requires JavaScript. You can try searching on GitHub
20+
using the links below.
21+
<ul>
22+
<li>
23+
<a href="https://github.com/search?q=org%3Acreativecommons+is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+-linked%3Apr">
24+
issues labeled "good first issue"
25+
</a>
26+
</li>
27+
<li>
28+
<a href="https://github.com/search?q=org%3Acreativecommons+is%3Aissue+is%3Aopen+label%3A%22help+wanted%22+-linked%3Apr">issues
29+
labeled "help wanted"
30+
</a>
31+
</li>
32+
<li>
33+
<a href="https://github.com/search?q=org%3Acreativecommons+is%3Apr+is%3Aopen+label%3A%22help+wanted%22">
34+
PRs labeled "help wanted"
35+
</a>
36+
</li>
37+
</ul>
38+
</noscript>
39+
</div>
40+
</div>
41+
</div>
42+
<script>
43+
// Transfer all issues to the JavaScript domain
44+
window.issues =
45+
{{ bag('issues.issues')|tojson }}
46+
</script>
47+
{% endblock %}

themes/vocabulary_theme/templates/layout.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,11 @@
7676
<div class="navbar-dropdown">
7777
{% for href, title in [
7878
['/contributing-code', 'Contribution Guidelines'],
79+
['/contributing-code/projects', 'Project List'],
80+
['/contributing-code/issue-finder', 'Issue Finder'],
7981
['/contributing-code/pr-guidelines', 'Pull Request Guidelines'],
8082
['/contributing-code/github-repo-guidelines', 'GitHub Repo Guidelines'],
8183
['/contributing-code/repo-labels', 'Repository Labels'],
82-
['/contributing-code/projects', 'Project List'],
8384
['/contributing-code/usability', 'Usability'],
8485
] %}
8586
<a class="navbar-item" href="{{ href|url }}">{{ title }}</a>

themes/vocabulary_theme/templates/page-with-toc.html

+8-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ <h1>{{ this.title }}</h1>
1919
{% if this.is_child_of('/contributing-code') %}
2020
<aside class="menu sidebar-menu">
2121
<ul class="menu-list">
22+
<li>
23+
<a class="{% if this.path == '/contributing-code/projects' %} is-active {% endif%} link" href="{{ '/contributing-code/projects'|url }}">Project List</a>
24+
</li>
25+
<hr class="divider">
26+
<li>
27+
<a class="{% if this.path == '/contributing-code/issue-finder' %} is-active {% endif%} link" href="{{ '/contributing-code/issue-finder'|url }}">Issue Finder</a>
28+
</li>
29+
<hr class="divider">
2230
<li>
2331
<a class="{% if this.path == '/contributing-code' %} is-active {% endif%} link" href="{{ '/contributing-code'|url }}">Contribution Guidelines</a>
2432
<ul>
@@ -28,10 +36,6 @@ <h1>{{ this.title }}</h1>
2836
</ul>
2937
</li>
3038
<hr class="divider">
31-
<li>
32-
<a class="{% if this.path == '/contributing-code/projects' %} is-active {% endif%} link" href="{{ '/contributing-code/projects'|url }}">Project List</a>
33-
</li>
34-
<hr class="divider">
3539
<li>
3640
<a class="{% if this.path == '/contributing-code/usability' %} is-active {% endif%} link" href="{{ '/contributing-code/usability'|url }}">Usability</a>
3741
</li>

webpack/js/components.js

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import VueSelect from 'vue-select';
2+
3+
export const IssueLabel = {
4+
template: `
5+
<span class="gh-label" :class="className">
6+
{{ name }}
7+
</span>`,
8+
data() {
9+
return {
10+
labels: window.labels
11+
}
12+
},
13+
props: {
14+
name: {
15+
type: String,
16+
required: true
17+
}
18+
},
19+
computed: {
20+
/**
21+
* Get the name of the class to apply to the label based on the group to
22+
* which it belongs. Falls back to miscellaneous if the label does not
23+
* belong to a group or a class cannot be identified.
24+
*
25+
* @returns {string} the name of the class to apply to the label
26+
*/
27+
className() {
28+
return window.categories[this.name] || 'miscellaneous'
29+
}
30+
}
31+
}
32+
33+
export const IssueCard = {
34+
template: `
35+
<div class="card entry-post vertical margin-top-normal padding-normal">
36+
<h4 class="card-title b-header margin-bottom-small">
37+
{{ issue.title }}
38+
</h4>
39+
<p class="is-size-6">
40+
<a
41+
:href="issue.html_url"
42+
target="_blank">
43+
<span class="has-color-forest-green">
44+
{{ issue.repo }}#{{ issue.number }}
45+
</span>
46+
<i
47+
class="icon external-link has-color-forest-green is-size-7"
48+
:style="{ verticalAlign: 'baseline' }">
49+
</i>
50+
</a>
51+
&nbsp;&nbsp;opened on {{ dateCreated }}.
52+
</p>
53+
<div class="labels margin-top-small">
54+
<IssueLabel
55+
v-for="(name, index) in issue.labels"
56+
:key="index"
57+
:name="name"/>
58+
</div>
59+
</div>`,
60+
components: {
61+
IssueLabel
62+
},
63+
props: {
64+
issue: {
65+
type: Object,
66+
required: true
67+
},
68+
},
69+
computed: {
70+
dateCreated() {
71+
const dateCreated = new Date(this.issue.createdAt*1000)
72+
return dateCreated.toLocaleDateString()
73+
}
74+
}
75+
}
76+
77+
export const App = {
78+
el: '#vue-app',
79+
template: `
80+
<div class="find-issues">
81+
<div class="columns">
82+
<div class="column is-one-quarter">
83+
<form id="filters">
84+
<label for="skills">
85+
<strong>Skill set</strong><br/>
86+
Choose up to three skills that you would like to see issues for.
87+
</label>
88+
<VueSelect
89+
v-model="filters.skills"
90+
id="skills"
91+
name="skills"
92+
placeholder="No preference"
93+
:options="options.skills"
94+
:reduce="skill => skill.toLocaleLowerCase()"
95+
:selectable="() => filters.skills.length < 3"
96+
multiple/>
97+
<br/>
98+
<label for="experience">
99+
<strong>Experience</strong><br/>
100+
Is this your first time contributing to CC?
101+
</label>
102+
<VueSelect
103+
v-model="filters.experience"
104+
id="experience"
105+
name="experience"
106+
:options="options.experiences"
107+
label="name"
108+
:reduce="experience => experience.code"
109+
:clearable="false"/>
110+
</form>
111+
</div>
112+
<div class="column">
113+
<template v-if="filteredIssues.length">
114+
<IssueCard
115+
v-for="(issue, index) in filteredIssues"
116+
:key="index"
117+
:issue="issue"/>
118+
</template>
119+
<p
120+
v-else
121+
class="margin-top-normal">
122+
No results.
123+
</p>
124+
</div>
125+
</div>
126+
</div>`,
127+
components: {
128+
VueSelect,
129+
IssueCard
130+
},
131+
data() {
132+
return {
133+
options: {
134+
skills: window.skills,
135+
experiences: [
136+
{name: 'Yes, it is', code: 'beginner'},
137+
{name: 'No, it isn\'t', code: 'experienced'}
138+
]
139+
},
140+
filters: {
141+
skills: [],
142+
experience: 'experienced'
143+
},
144+
issues: []
145+
}
146+
},
147+
computed: {
148+
/**
149+
* Get a filtered list of issues matching the chosen skill labels.
150+
*
151+
* @returns {array} the array of filtered issues
152+
*/
153+
filteredIssues() {
154+
return window.issues.filter(issue => {
155+
// Check experience match
156+
if (this.filters.experience === 'beginner' && !issue.labels.includes('good first issue')) {
157+
return false
158+
}
159+
160+
// Check skill set match
161+
const joinedLabels = issue.labels.join(',')
162+
if (this.filters.skills.length && !this.filters.skills.some(skill => joinedLabels.includes(skill))) {
163+
return false
164+
}
165+
166+
return true
167+
}).sort((a, b) => b.createdAt - a.createdAt)
168+
}
169+
}
170+
}

webpack/js/hydration.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
export const hydrateAppWithData = () => {
2+
window.skills = Array.from( // Convert back into an array
3+
new Set( // Remove duplicates
4+
Object.values(window.skills)
5+
.flat() // Combine all skills
6+
.map(skill => skill.split('/')[0]) // Keep only the prefix
7+
)
8+
)
9+
10+
window.categories = {}
11+
window.labels.groups.forEach(group => {
12+
group.labels.forEach(label => {
13+
let name = label.name
14+
if (group.is_prefixed !== false) {
15+
name = `${group.name}: ${name}`
16+
}
17+
if (label.has_emoji_name !== false) {
18+
name = `${label.emoji} ${name}`
19+
}
20+
let styleName = label.color;
21+
if (/^[A-Z]+$/.test(styleName)) {
22+
styleName = `${group.name}-${styleName.toLocaleLowerCase()}`
23+
} else {
24+
styleName = group.name
25+
}
26+
window.categories[name] = styleName
27+
})
28+
})
29+
window.labels.standalone.forEach(label => {
30+
let name = `${label.emoji} ${label.name}`
31+
window.categories[name] = 'miscellaneous'
32+
})
33+
window.skills.forEach(skill => {
34+
let name = `💪 skill: ${skill.toLocaleLowerCase()}`
35+
window.categories[name] = 'skill'
36+
})
37+
}

0 commit comments

Comments
 (0)