To run Korp frontend the following is needed:
- A Korp backend with corpora installed and configured
- Node.js
- Yarn
The easiest way to try the frontend locally is to:
- Clone and go to the root of this repository (https://github.com/spraakbanken/korp-frontend) on your machine
- Run
yarn
to install dependencies - Follow the instructions under Configuration. At least a
config.yml
is needed. - Run
yarn start
yarn start
uses Webpack DevServer. It builds the code and starts
a web server locally, by default on port 9111
. When the configuration is changed, the server automatically
rebuilds everything. This makes testing your setup really easy.
When the frontend instance feels ready to deploy, the code must be built for production using:
yarn build
The build is put in the dist
-directory. The build only contains resources that any
browser understands, such as HTML, Javascript, CSS, images and fonts. Therefore you can try out the build locally by using any web server,
Node.js and the development dependencies are not needed anymore.
A really easy way if you have Python (version 3) installed, is:
cd dist; python -m http.server 8080
If everything still works as expected, the contents of dist
can be deployed to production.
In ideal cases, no changes needs to be done in the frontend code. Instead all configuration will reside in another directory.
Throughout this document, configDir
will refer to either app
or the configured directory.
The only file in the configuration directory that is mandatory to make Korp work is:
config.yml
These files are necessary in some cases:
modes/*mode.js
translations/*.json
Mode files should only be necessary for modes with custom functionality (in short, a mode is a collection of corpora that may have different functionality and is described later).
If a new language needs to be added, see Adding Languages for instructions.
For more advanced use cases there is also the possibility to custommize by adding add own code or styling, see Customizing.
To use a configuration directory,
add a file called run_config.json
file in the root of the repository with the following content:
{
"configDir": "../path/to/my/configuration/directory"
}
The directory pointed out should be the one containing config.yml
.
Språkbanken's configuration repository is https://github.com/spraakbanken/korp-frontend-sb. It can be used as a supplement to this documentation. Make sure to check out the branch corresponding to the branch you are using in the main repository.
Note: In the spring 2022 there was a rewrite where most of the frontend configuration moved to the backend. We also changed format from camel case to
snake case. So wordpictureTagset
became word_picture_tagset
. Also config.js
was turned into a YAML-file, config.yml
.
Many of the settings listed here can be given in a modes-file in the backend instead of config.yml
. For example autocomplete
could be wanted in
one mode and not another. See the
backend documentation for more
settings that affect the frontend.
Attributes that must be added to config.yml
, and doesn't work in modes-files:
- korp_backend_url - URL to Korp's backend
- languages - Array of objects with language code and translation of supported UI languages, for example:
- value: eng label: English - value: swe label: Svenska
Others:
-
auth_module - String or object. See Authentication
-
autocomplete - Boolean. See auto completion menu
-
common_struct_types - Object with attribute name as a key and attribute definition as value. Attributes that may be added automatically to a corpus. See backend documentation for more information about how to define attributes.
-
config_dependent_on_authentication - Boolean. If true, backend config will not be fetched until login check has finished.
-
corpus_info_link - Object. Use this to render a link for each corpus in the corpus chooser.
- url_template - String or translation object. A URL containing a token "%s", which will be replaced with the corpus id.
- label - String or translation object. The label is the the same for all corpora.
-
default_options - See Operators.
-
default_overview_context - The default context for KWIC-view. Use a context that is supported by the majority of corpora in the mode (URLs will be shorter). E.g.:
"1 sentence"
. For corpora that do not support this context an additional parameter will be sent to the backend based on thecontext
-setting in the corpus. -
default_reading_context - Same as default_overview_context, but for the context-view. Use a context larger than the default_overview_context.
-
default_within - An object containing the structural elements of a corpus.
default_within
is used unless a corpus overrides the setting usingwithin
. Example:default_within: sentence: sentence
In simple search, we will search within the default and supply extra information for the corpora that do not support the default.
In extended search, the default
within
will be used unless the user specifies something else. In that case the user's choice will be used for all corpora that support it and for corpora that do not support it, a supportedwithin
will be used. -
default_language - String. The default interface language. Default:
"eng"
-
description - String. Any HTML content to show on frontpage until search is made.
-
enable_backend_kwic_download - Boolean. Backend download, depends on backend download functionality.
-
enable_frontend_kwic_download - Boolean. Frontend download. Gives CSV created by same data as available in the KWIC.
-
frontpage - Object. Settings for what to show under the search form until a search is made.
- corpus_updates - Boolean. Enables a listing of most recently updated corpora.
- examples - List of objects. A random selection of three of these are shown on the frontpage as search links.
- label: String or translation object.
- params: Object. This is translated to URL search params when the link is clicked.
- hint: String or translation object. Can contain HTML.
-
get_corpus_ids - Async function returning a list of strings. The corpus ids are passed as the
corpus=
param to the<korp_backend_url>/corpus_config?mode=<mode>
call, see thecorpus_config
API. -
group_statistics - List of attribute names. Attributes that either have a rank or a numbering used for multi-word units. For example, removing
:2
fromta_bort..vbm.1:2
, to get the lemgram of this word:ta_bort..vbm.1
. -
has_timespan - Boolean. If the backend supports the
timespan
call, used in corpus chooser for example. Default:true
-
hits_per_page_values - Array of integer. The available page sizes. Default:
[25, 50, 75, 100]
-
hits_per_page_default - Integer. The preselected page size. Default:
hits_per_page_values[0]
-
input_case_insensitive_default - Boolean. Decides if the simple search input should be case-insensitive by default.
-
iso_languages - A map of two-letter ISO language codes to three-letter. Only used for fixing old links. Default: See
settings.js
-
map_center - See Map
-
map_enabled - Boolean. See Map
-
matomo - Object. Enable analytics with a Matomo instance.
- url: String. The URL of the Matomo instance, including trailing slash.
- site: Integer. The site ID that Matomo has assigned for the Korp instance.
- It is also possible to override each value underneath keys corresponding to
ENVIRONMENT
values, e.g:matomo: url: https://matomo.example.com/ site: 1 production: site: 2
-
news_url - See News widget
-
reduce_word_attribute_selector - String,
union
/intersection
. For the "compile based on" configuration in statistics, show all selected corpora word attributes or only the attributes common to selected corpora. Warning: if set to"union"
, the statistics call will fail if user selects an attribute that is not supported by a selected corpus. -
reduce_struct_attribute_selector - Same as reduce_word_attribute_selector, but for structural attributes.
-
statistics - Boolean. Enable statistics search. Default:
true
-
statistics_case_insensitive_default - Boolean. Decides if the "Reduce by" option should be case-insensitive by default.
-
statistics_search_default - Boolean. Decides if "Show statistics" will be checked or not when loading Korp. Default:
true
-
visible_modes - Integer. The number of modes to show links to. If there are more modes than this value, the rest will be added to a drop-down. Default:
6
-
word_label - Translation object. Translations for "word". Add if you need support for other languages. Default:
swe: ord eng: word
-
word_picture - Boolean. Enable/disable the word picture.
-
word_picture_tagset - See Word picture
-
word_picture_conf - See Word picture
Add corpora_<lang>.json
files to <configDir>/translations
where lang is replaced with a
language you want to support. It is also possible to put translations
to be used in custom components for extended search and sidebar here.
Files prefixed with locale
in the code base controls translations that are hard-coded into the
application and thus it should not be necessary to change these, unless making code changes.
To add a new language in the frontend, for example Lithuanian, add a corpora-lit.json
and locale-lit.json
. locale-lit.json
may be copied from an existing locale-file and
then translated. Then add the language in config.yml
:
languages:
- value: swe
label: Svenska
- value: eng
label: English
If for some reason one wants to translate the language names in the language picker, label
may be an object with translations:
label:
swe: "Svenska"
eng: "Swedish"
To make Lithuanian the default language, use:
default_language: lit
To enable full localization (dates in a date picker, commas or dots for decimal separator, etc.), an extra file
is necessary. Download angular-locale_lt.js
from here:
https://github.com/angular/bower-angular-i18n
Put the file in <configDir>/translations/
, but rename it using three letter language codes, to: angular-locale_lit.js
Each Korp installation has a series of Modes in the top left corner, which are useful for presenting different faces of Korp that might have different layouts or functionality.
When Korp is loaded, it looks for the mode
query parameter:
https://<frontend url>/?mode=kubhist
If no mode is given, mode is default
.
It then looks for mode-specific code in <configDir>/modes/<mode>_mode.js
. Mode code may overwrite values from config.yml
by altering the settings
object imported from @/settings
.
It then looks for settings for this specific mode, the corpus config. If it exists at <configDir>/modes/<mode>_corpus_config.json
, it will be loaded from there. Otherwise, it retrieves it from the backend:
https://<korp_backend_url>/corpus_config?mode=<mode>
Normally, the mode param is enough for the backend to know what corpora to include. Alternatively, it is possible to specify corpus ids in the corpus=
param, by assigning a function to the get_corpus_ids
setting (in <mode>_mode.js
). The function can be async and should return a list of corpus ids.
See the corpus_config
API for more information.
By enabling the parallel option, a mode can be adapted for parallel corpora.
Additional settings in config.yml
(may also be given by the backend for the mode):
parallel
- Set to true
to enable parallel functionality
start_lang
- language that should be the default search language.
Korp features an auto completion list for searches in the Simple Search as well as in Extended for those corpus
attributes configured to use autoc
-directive. This is implemented using an
Angular.js directive autoc
that calls Karp's auto completion function.
Using Karp, Korp can autocomplete senses and lemgrams. To disable add the following to config.yml
:
autocomplete: false
When clicking on a word in the KWIC (or text in the reading mode), a sidebar appears with information about the
current word. By default, all the attributes listed under pos_attributes
, struct_attributes
and custom_attributes
are shown. Which attributes to show and how they should be displayed are customizable. See Attribute settings.
The order of the attributes arrays determine the order in the sidebar. Custom attributes are added to the end of their respective category.
The frontend features a number of components that can be used for the attributes in extended search. For example, dropdowns with a static list of search alternatives, autocompletion menus etc.
The following examples are in YAML, that is used in the backend configuration. Simple usage:
attribute_name:
extended_component: autocExtended
Some of the components have support for options. This is the format to use then:
attribute_name:
extended_component:
name: datasetSelect
options:
sort: false
If none of the built in components fit the use case, see customizing extended search.
The built in components are:
Supported options:
- sort, default is true
To be documented
To be documented
To be documented
To be documented
To be documented
There is a built-in component for searching time intervals in extended search.
To use it, simply set extended_component: dateInterval
in the attribute's configuration.
There is also another possibility. If the corpus has time data enabled, as explained here:
https://github.com/spraakbanken/korp-backend#time-data
Then time interval will be added automatically as a search alternative, but this needs to be added to config.yml
:
common_struct_types:
date_interval:
label: "time interval"
hide_sidebar: 'true'
hide_compare: 'true'
hide_statistics: 'true'
opts: false
extended_component: dateInterval
default_options
lists the most common set of wanted operators in extended search. They will be used unless an
attribute specifies another set of operators using opts
.
Språkbanken's default_options
is:
default_options:
is: =
is_not: '!='
starts_with: ^=
contains: _=
ends_with: '&='
matches: '*='
matches_not: '!*='
The values in this object refers to internal operators used by Korp frontend only. The purpose of the internal operators are, for example, to know if values need to be escaped/unescaped with regards to special regexp characters.
The object above is suitable for simple words/strings where one can be interested in searching for affixes.
If there is a known value set of an attribute, as for example in POS-tagging, this is a suitable value for opts
:
opts:
is: "="
is_not": "!="
And if the attribute has a set of values instead of a single one, but regexp and affixes should be supported, this:
opts:
contains: incontains_contains
ends_with: ends_with_contains
is: contains
is_not: not contains
matches: regexp_contains
matches_not: not_regexp_contains
starts_with: starts_with_contains
And if no regexp or affix-search is needed:
opts:
is: contains
is_not: not contains
The keys in these objects are translation keys and the values are used in an internal "CQP-format". The values will be translated to
proper CWB-supported operators before being sent to the backend. For example regexp_contains
will be translated to just contains
,
while the operand will not be escaped. starts_with_contains
, will be translated to contains
and the operand will be escaped and then have .*
added
to the end.
The word picture-config object looks like this:
word_picture_conf:
pos_tag:
- table_def1
- table_def2
...
where table_defX
is an array of objects that describe the resulting word picture table. table_def1
above might look like this:
- rel: subject
css_class: color_blue
- _
- rel: object
css_class: color_purple
- rel: adverbial
css_class: color_green
The _
refers to the placement of the lookup word in the table order. The value for rel
refers to a key in word_picture_tagset
looking like this:
word_picture_tagset:
subject: ss
object: obj
adverbial: adv
preposition_rel: pa
pre_modifier: at
post_modifier: et
adverbial2: aa
...
The values are the actual relations returned by the backend. The relation used is determined by field_reverse
. If field_reverse
is false
(default), dep
is used, else head
. If you find yourself with a table full of the search word just flip the field_reverse
switch.
css_class
simply gives a class to the column, useful for applying background color. The last supported attribute is alt_label
, used for when another value than the relation name should be used for the table header.
Korp's map uses annotations to get locations. The user selects rows from the statistics table and points derived from different rows will have different colors. The selected corpora must have structural attributes with location data in them. The format is Fukuoka;JP;33.6;130.41667
- the location name, the country, latitude and longitude separated by ;
.
Also the name of the attribute must contain "__"
and "geo"
to show up in the list of supported attributes.
map_enabled
- Boolean. Enable/disable the map functionality.map_center
- Where the center of the map should be located when user opens map. Example:
map_center:
lat: 62.99515845212052
lng: 16.69921875
zoom: 4
By setting news_url
, the news widget is enabled. The widget simply fetches a YAML file from the given URL. Short example of such a file, including only one news item with its title and body in two languages and a date:
- title:
swe: "Ny korpus: Tvåkammarriksdagen"
eng: "New corpus: Tvåkammarriksdagen"
body:
swe: <p><a href="...">Tvåkammarriksdagen</a> finns nu i Korp.</p>
eng: <p><a href="...">Tvåkammarriksdagen</a> is now available in Korp.</p>
created: 2023-11-30
Local storage is used to remember when the user last checked the news. If there are new items, the UI will change to reflect this.
Korp comes with two implementations of login. Choose implementation using auth_module
in config.yml
.
Either just give your chosen implementation like this:
auth_module: "name"
Or as an object, if options are needed:
auth_module:
module: "name"
options:
an_option: true
The module name is basic_auth
. This is the default implementation. It has two options:
- show_remember: Default
true
. Whether or not to show the "Remember me" option. - default_value_remember: Default
false
. If the remember checkbox is ticke or not by default
If the login should be remembered, the user's credentials are stored in local storage.
The module name is federated_auth
. It checks if a JWT is available at an endpoint and uses the JWT in
any subsequent communication with the backend. If the user clicks Login, they are redirected to
a login service. If the user clicks Logout, they are redirected to a logout service. The options are:
- jwt_url
- login_service
- logout_service
It is possible to define your own authentication module and set this using auth_module
. The code should be
located in custom/
and support the functions used in components/auth.js
.
Corpora and their attrbutes are configured in the backend, but most of the available settings are frontend related. These are the available configuration parameters for attributes.
- label: Label to display wherever the attribute is shown.
- display_type: Set to
hidden
to fetch attribute, but never show it in the frontend. Seehide_sidebar
,hide_statistics
,hide_extended
andhide_compare
for more control. - extended_component: For available components, see extended components. For writing custom components, see customizing extended search.
- external_search: Link with placeholder for replacing value. Example
https://spraakbanken.gu.se/karp/#?search=extended%7C%7Cand%7Csense%7Cequals%7C<%= val %>
- group_by: Set to either
group_by
orgroup_by_struct
. Should only be needed for attributes withis_struct_attr: true
. Those attributes are by default sent asgroup_by_struct
in the statistics, but can be overridden here. - hide_sidebar:
boolean
. Defaultfalse
. Hide attribute in sidebar. - hide_statistics:
boolean
. Default:false
. Should it be possible to compile statistics based on this attribute? - hide_extended:
boolean
. Default:false
. Should it be possible to search using this attribute in extended? - hide_compare:
boolean
. Default:false
. Should it be possible to compare searches using this attribute? - internal_search:
boolean
. Should the value be displayed as a link to a new Korp search? Only works for sets. Searches for CQP-expression:[<attrName> contains "<regescape(attrValue)>"]
- is_struct_attr:
boolean
. Iftrue
the attribute will be treated as a structural attribute in every sense except it will be included in theshow
query parameter instead ofshow_struct
for KWIC requests. Useful for structural attributes that extend to smaller portions of the text than the selected context, such as name tagging. - opts: this represents the auxiliary select box where you can modify the input value. See Operators section for format and more information.
- order: Order of attribute in the sidebar. Attributes with a lower
order
-value will be placed above attributes with a higherorder
-value. - pattern: HTML snippet with placeholders for replacing values. Available is
key
(attribute name) andvalue
. Also works for sets. Example:'<p style="margin-left: 5px;"><%=val.toLowerCase()%></p>'
- sidebar_component: See Customizing sidebar.
- sidebar_info_url:
string
(URL). If defined and non-empty, add an info symbol ⓘ for the attribute in the sidebar, linking to the given URL. This can be used to link to an explanation page for morphosyntactic tags, for example. - sidebar_hide_label:
boolean
. Iftrue
, do not show the localized attribute label and the colon following it in the sidebar, only the attribute value. This can be used, for example, if thepattern
for the attribute includes the label but the label should be shown in the attribute lists of the extended search or statistics. - stats_cqp: See Rendering attribute values in the statistics view.
- stats_stringify: See Rendering attribute values in the statistics view.
- translation: An object containing translations of possible values of the attribute, in this format:
This replaces value-translation in the translation-files, and also the old attribute
ROOT: eng: Root swe: Rot ++: eng: Coordinating conjunction swe: Samordnande konjunktion +A: eng: Conjunctional adverbial swe: Konjuktionellt adverb
translationKey
. - type: Possible values:
- "set" - The attribute is formatted as "|value1|value2|". Include contains and not contains in
opts
. In the sidebar, the value will be split before formatted. When using compile /groupby
on a "set" attribute in a statistics request, it will be added tosplit
. - "url" - The value will be rendered as a link to the URL and possibly truncated if too long.
- "set" - The attribute is formatted as "|value1|value2|". Include contains and not contains in
Custom attributes are attributes that do not correspond to an attribute / annotation in the backend. They are mainly used to present information in the sidebar that combines values from other attributes.
- custom_attributes: creates fields in the sidebar that have no corresponding attribute in the backend. Useful for combining two different attributes. All settings concerning sidebar format for normal attributes apply in addition to:
- custom_type:
"struct"
/"pos"
- decides if the attribute should be grouped under word attributes or text attributes. - pattern: Same as pattern for normal attributes, but
struct_attrs
andpos_attrs
also available. Example:'<p style="margin-left: 5px;"><%=struct_attrs.text_title - struct_attrs.text_description%></p>'
- custom_type:
Korp does runtime DOM manipulation when the user changes language. Using an Angular filter to specify which translation key looks like this:
<div>{{'my_key' | loc}}</div>
For proper reactivity, it is generally necessary to use loc:lang
or even loc:$root.lang
, instead of just loc
.
Add my_key
to <configDir>/translations/corpora-<lang>.json
for all lang
.
[Deprecation warning] Before the Angular approach we used the rel
attribute, like so (but you shouldn't any more):
<span rel="localize[translation_key]">...</span>
Define your own components as a map in custom/components.js
. component
will be added as a component with name componentName
to the Angular app.
import component from 'custom/myComponentFile'
export default {
componentName: component
}
These can then be used in other custom components / extended / sidebar or as reading mode-components.
Remember that in Angular, if you use myComponentName
as a name of a component, you must use
my-component-name
when using the component in markup.
In custom/extended.js
, we can define custom (non-Angular) components to be used in extended search:
export default {
complemgramExtended: {
template: `<input type="text" ng-model="model" />
`,
controller: [
"$scope", function($scope) {
$scope.$watch("input; () => ...)
...
}],
},
attr: {
template: `
<select ...>
`,
controller: ["$scope", "$uibModal", function($scope, $uibModal) {
if($scope.show) $uibModal.open
}
},
...
}
Template is an Angular.js template string and controller is an Angular.js controller function.
Make sure to set $scope.model
as the final result to be used in the CQP-query.
complemgramExtended
can then be used as key for extendedComponent
in the configuration files.
attributes: {
complemgram: {
label: "Compounds",
extendedComponent: "complemgramExtended",
}
}
escaper
is a directive that takes the user's input and escapes any regexp characters before saving it to scope.model
.
When the model changes it automatically de-escapes any regexp characters before showing the value to the user.
Input must be saved to scope.input
for it to work. Example: <input ng-model="input" escaper>
In custom/sidebar.js
, we can define custom components to be used in the sidebar:
export default {
imageSidebar: {
template: `<img ng-src="myImg" />
`,
controller: [
"$scope", function($scope) {
$scope.myImg = $scope.sentenceData["text_mediafilepath"]
...
}],
},
...
}
Useful for having e.g. a modal window pop up, or for rendering a small video player in the sidebar, or for anything else that isn't simple text or a link. Also when combining values from several attributes.
Use imageSidebar
as key for sidebarComponent
in the configuration files.
Data about the search, the current token and current attribute is stored in a number of variables on $scope
:
$scope.type
: "struct" / "pos", the type of the attribute$scope.key
: Name of the attribute$scope.value
: Value of the attribute$scope.attrs
: The attribute definition from the config$scope.wordData
: The values of the positional attributes for current token$scope.sentenceData
: The values of the structural attributes for current token / structure$scope.tokens
: All the tokens in the current sentence
Note: The component not an actual Angular.js component. It will be added to the interface by manually creating a new scope and using $controller
to instantiate the controller and $compile
to instantiate the template.
Define your own rules for rendering values and generating CQP-expressions for certain attributes.
When configuring an attribute that needs special handling, use the stats_cqp
and stats_stringify
keywords:
const myAttribute = {
label: "category",
order: 80,
stats_stringify: "customStringify",
stats_cqp: "customCQP",
}
Then create custom/statistics.js
and define the functions there:
export default {
customStringify: (values) => values.join(' == '),
customCQP: (tokens) => "(" + tokens.map(item => `_.cat="${item}"`).join(" | ") + ")",
}
Rendering values and generating CQP can also be controlled by editing app/config/statistics_config.js
, but
of course it is best to avoid editing the actual code if it is possible.
If you need to merge rows or otherwise alter the table structure, you can extend the StatsProxy
class and do statsProxyFactory.setClass(MyStatsProxy)
.
Add all custom pretty-printing to custom/stringify.js
. Example file:
import { lemgramToHtml, saldoToHtml } from "@/util"
export const {
sense: (sense) => saldoToHtml(sense, true),
lemgram: (str) => lemgramToHtml(str, true),
complemgram: (str) => str.split('+').map((lemgram) => lemgramToHtml(lemgram, true)).join('+')
}
Note that no changes in the attribute-configuration is needed here. Korp will pick up the functions automatically based on the name of the attribute. Will be used in sidebar, statistics, extended, etc.
Enable the standard reading mode by using this setting on a corpus:
reading_mode: true
When clicking on a word in the KWIC a link will be added to the sidebar. Clicking this link opens a new tab where the entire text is shown.
The corpus must have the structural attribute text__id
, which should be a unique ID in the corpus. _head
and _tail
are also needed and should contain the whitespace before and after the token. It is optional to put whitespace in both attributes. The simplest use case is to just put the trailing whitespace in _tail
of that token and leave _head
empty. The frontend will assume that any corpus with reading_mode: true
will have these attributes.
It is possible to write a custom reading component. See this file for an example. See Components for documentation on custom components.
Here is where we present details on how to do code changes in Korp. Changes that improve the code base in may be submitted using pull requests on Github.
All configuration parameters in config.yml
are added to a global settings
-object. For example:
my_parameter: my value
Will make settings["my_parameter"]
available in the app.
Use snake_case
when defining new attributes in config.yml
. Add a default value for the new attribute in app/scripts/settings.js
, if needed.
CQP queries are of course parsed in the backend to perform searching. But they are also parsed in the frontend, for programmatic manipulation etc. The frontend parser is written in Peggy syntax: CQPParser.peggy. It covers only some of the full CQP syntax supported by the backend, and it is quite expected to throw errors when parsing user-crafted queries.
To rebuild JS code from the Peggy file, do:
cd app/scripts/cqp_parser
npx peggy --format es -d _:lodash CQPParser.peggy
It is OK to create a pull request without a corresponding issue, but if the pull request aims to fix an issue, it should be clearly stated.
Discussions can be made in the pull request.
In general, the pull request should be for the dev
-branch.
If the pull request is a fix for a critical bug, a pull request can be made for master
. The changes should of course also be merged to the dev
-branch, but this will be made by Språkbanken.
Try to answer these questions in your pull request description:
- What has been changed?
- Why was the change made? (Can be excluded, if it is obvious).
The pull request may include changes on multiple topics, if they are related. It is OK to include as many commits as needed in the request. The commits will not be squashed when merging.
If the commit depends on new functions in the backend, add a note of which backend version/commit, is needed for the code to work.
The code should be formatted using Prettier, with the supplied .prettierrc
. It is possible to make your editor do this automatically on save. Otherwise, run prettier before committing (yarn run format
).
TypeScript is used to provide typing and to transpile new language features for older browsers.
Use modern features where it looks good. Always use const
or let
instead of var
.
Identifiers should be in camel case (although our old Korp code may still have some identifiers that uses snake case).
When using the settings object, use settings["my_parameter"]
instead of settings.my_parameter
. This is to emphasize that settings
should be viewed as a data structure that is holding values, and to avoid using snake case in code.
Aim to write code that is easily understood, and supplement with comments as needed. Keep comments up to date while making code changes.
Files should be named using snake case: my_file.js
.
- Throw a specific error when something fails (e.g.
KorpBackendError
if an API request fails) - Catch errors where they can be handled, as low as possible in the call hierarchy (e.g. log and continue, or show a message)
Unhandled errors are caught and shown by window
listeners and the $exceptionHandler
service.
Do not add new dependencies to package.json
, unless it cannot absolutely be avoided, and the new dependency is a widely used library in active development.
We strive to write everything that is possible as an Angular component: https://docs.angularjs.org/guide/component
Avoid using directives and controllers.
Angular will automatically pass the services demanded by controllers, see https://docs.angularjs.org/guide/di
Do this (inline array annotation):
controller: [
"$scope",
function ($scope) { ... }
]
Do not do (implicit annotation):
controller: function ($scope) { ... }
This will fail when building the frontend (yarn build
), due to minification by Webpack. It probably works with yarn start
, so beware.
Update this document if needed.
Previous test code has been flaky and underprioritized, and is now removed. To examine or restore it, filter the git log for changes to the test/
folder.