Merged in feature/content-block-instrument-type-MS-480 (pull request #119)

Feature/content block instrument type MS-480

Approved-by: Christian Cueni
This commit is contained in:
Ramon Wenger 2022-09-19 13:05:44 +00:00
commit 6476d09f6d
18 changed files with 172 additions and 25 deletions

View File

@ -26,7 +26,11 @@ describe('Instruments on Module page', () => {
title: 'Some Chapter',
contentBlocks: [
{
'type': 'base_communication',
'type': 'instrument',
instrumentCategory: {
id: 'category-id',
name: 'Sprache & Kommunikation'
},
'title': 'Das Interview',
'contents': [
{
@ -40,6 +44,7 @@ describe('Instruments on Module page', () => {
{
'type': 'normal',
'title': 'Normaler Block',
instrumentCategory: null,
'contents': [
{
type: 'text_block',

View File

@ -5,6 +5,7 @@
>
<div
:class="specialClass"
:style="instrumentStyle"
class="content-block"
data-cy="content-block"
>
@ -43,6 +44,7 @@
<h3
class="content-block__instrument-label"
data-cy="instrument-label"
:style="instrumentLabelStyle"
v-if="instrumentLabel !== ''"
>
{{ instrumentLabel }}
@ -129,13 +131,37 @@
specialClass() {
return `content-block--${this.contentBlock.type.toLowerCase()}`;
},
isInstrumentBlock() {
return !!this.contentBlock.instrumentCategory;
},
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
instrumentStyle() {
if (this.isInstrumentBlock) {
return {
backgroundColor: this.contentBlock.instrumentCategory.background
};
}
return {};
},
instrumentLabel() {
const contentType = this.contentBlock.type.toLowerCase();
if (contentType.startsWith('base')) { // all instruments start with `base`
if (contentType.startsWith('base')) { // all legacy instruments start with `base`
return instrumentCategory(contentType);
}
if (this.isInstrumentBlock) {
return instrumentCategory(this.contentBlock.instrumentCategory.name);
}
return '';
},
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
instrumentLabelStyle() {
if (this.isInstrumentBlock) {
return {
color: this.contentBlock.instrumentCategory.foreground
};
}
return {};
},
canEditContentBlock() {
return this.contentBlock.mine && !this.contentBlock.indent;
},
@ -316,6 +342,10 @@
}
}
&--instrument {
@include content-box-base;
}
/deep/ p {
line-height: 1.5;
margin-bottom: 1em;

View File

@ -8,6 +8,9 @@
<router-link
:to="{name: 'instrument', params: { slug: value.slug }}"
class="instrument-widget__button button"
:style="{
borderColor: value.foreground
}"
>
{{ $flavor.textInstrument }} anzeigen
</router-link>
@ -15,6 +18,7 @@
</template>
<script>
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
export default {
props: ['value'],
};

View File

@ -2,10 +2,17 @@
<a
:class="typeClass"
class="filter-entry"
:style="categoryStyle"
>
<span class="filter-entry__text">{{ text }}</span>
<span class="filter-entry__icon-wrapper">
<chevron-right class="filter-entry__icon" />
<span
:style="activeStyle"
class="filter-entry__icon-wrapper"
>
<chevron-right
:style="{fill: category.foreground}"
class="filter-entry__icon"
/>
</span>
</a>
@ -13,6 +20,7 @@
<script>
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql';
const ChevronRight = () => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight');
export default {
@ -31,7 +39,7 @@
},
category: {
type: Object,
default: () => {},
default: () => ({}),
},
},
@ -41,8 +49,8 @@
apollo: {
instrumentFilter: {
query: INSTRUMENT_FILTER_QUERY
}
query: INSTRUMENT_FILTER_QUERY,
},
},
data() {
@ -56,12 +64,31 @@
computed: {
isActive() {
if (!this.instrumentFilter.currentFilter) {
return this.type === '';
return this.id === '';
}
// eslint-disable-next-line
const [_, identifier] = this.instrumentFilter.currentFilter.split(':');
return this.type === identifier;
return this.id === identifier;
},
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
activeStyle() {
if (this.isActive) {
return {
backgroundColor: this.category.background,
};
}
return {};
},
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
categoryStyle() {
if (this.isCategory) {
return {
color: this.category.foreground,
};
}
return {};
},
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
typeClass() {
return {
'filter-entry--active': this.isActive,

View File

@ -25,6 +25,7 @@
import {INTERDISCIPLINARY, LANGUAGE_COMMUNICATION, SOCIETY} from '@/consts/instrument.consts';
import {instrumentCategory} from '@/helpers/instrumentType';
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
export default {
props: {
instrument: {

View File

@ -3,6 +3,12 @@ fragment ContentBlockParts on ContentBlockNode {
slug
userCreated
mine
instrumentCategory {
id
foreground
background
name
}
bookmarks {
uuid
note {

View File

@ -2,6 +2,8 @@ query InstrumentCategoriesQuery {
instrumentCategories {
name
id
foreground
background
types {
name
id

View File

@ -8,7 +8,7 @@ const instrumentType = (instrument) => {
} else {
return instrument.type.category.name;
}
return typeDictionary[category] || '';
return typeDictionary[category] || category || '';
};
const instrumentCategory = (instrument) => {

View File

@ -61,18 +61,25 @@
}
@mixin content-box($color-list) {
background-color: nth($color-list, 2);
@mixin content-box-base {
padding: 15px;
align-items: start;
border-radius: $default-border-radius;
/deep/ .button {
border-color: nth($color-list, 1);
background-color: $color-white;
}
}
@mixin content-box($color-list) {
@include content-box-base;
background-color: nth($color-list, 2);
/deep/ .button {
border-color: nth($color-list, 1);
}
}
@mixin desktop {
@media (min-width: 1200px) {
@content

View File

@ -81,9 +81,10 @@ def augment_fields(raw_data):
logger.error('Survey {} does not exist'.format(survey_id))
if _type == 'basic_knowledge' or _type == 'instrument':
_value = data['value']
basic_knowledge = BasicKnowledge.objects.get(pk=_value['basic_knowledge'])
instrument = BasicKnowledge.objects.get(pk=_value['basic_knowledge'])
_value.update({
'slug': basic_knowledge.slug
'slug': instrument.slug,
'foreground': instrument.new_type.category.foreground
})
data['value'] = _value

View File

@ -157,7 +157,7 @@ class ContentBlockFactory(BasePageFactory):
class Meta:
model = ContentBlock
type = factory.LazyAttribute(lambda x: random.choice(['normal', 'base_communication', 'task', 'base_society']))
type = factory.LazyAttribute(lambda x: random.choice(['normal', 'instrument', 'task',]))
contents = wagtail_factories.StreamFieldFactory({
'text_block': TextBlockFactory,

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.13 on 2022-09-15 13:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('books', '0036_alter_contentblock_contents'),
]
operations = [
migrations.AlterField(
model_name='contentblock',
name='type',
field=models.CharField(choices=[('normal', 'Normal'), ('base_communication', 'Instrument Sprache & Kommunikation'), ('task', 'Auftrag'), ('instrument', 'Instrument'), ('base_society', 'Instrument Gesellschaft'), ('base_interdisciplinary', 'Überfachliches Instrument')], default='normal', max_length=100),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.13 on 2022-09-15 13:40
from django.db import migrations
def migrate_instruments(apps, schema_editor):
ContentBlock = apps.get_model('books', 'ContentBlock')
ContentBlock.objects.filter(type__startswith='base_').update(type='instrument')
class Migration(migrations.Migration):
dependencies = [
('books', '0037_alter_contentblock_type'),
]
operations = [
migrations.RunPython(migrate_instruments, migrations.RunPython.noop)
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.13 on 2022-09-15 14:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('books', '0038_auto_20220915_1340'),
]
operations = [
migrations.AlterField(
model_name='contentblock',
name='type',
field=models.CharField(choices=[('normal', 'Normal'), ('task', 'Auftrag'), ('instrument', 'Instrument')], default='normal', max_length=100),
),
]

View File

@ -26,17 +26,13 @@ class ContentBlock(StrictHierarchyPage):
verbose_name_plural = 'Inhaltsblöcke'
NORMAL = 'normal'
BASE_COMMUNICATION = 'base_communication'
TASK = 'task'
BASE_SOCIETY = 'base_society'
BASE_INTERDISCIPLINARY = 'base_interdisciplinary'
INSTRUMENT = 'instrument'
TYPE_CHOICES = (
(NORMAL, 'Normal'),
(BASE_COMMUNICATION, 'Instrument Sprache & Kommunikation'),
(TASK, 'Auftrag'),
(BASE_SOCIETY, 'Instrument Gesellschaft'),
(BASE_INTERDISCIPLINARY, 'Überfachliches Instrument'),
(INSTRUMENT, 'Instrument'),
)
# blocks without owner are visible by default, need to be hidden for each class

View File

@ -2,6 +2,8 @@ import graphene
from graphene import relay
from graphene_django import DjangoObjectType
from basicknowledge.models import BasicKnowledge
from basicknowledge.queries import InstrumentCategoryNode
from books.models import ContentBlock
from books.schema.interfaces.contentblock import ContentBlockInterface
from books.utils import are_solutions_enabled_for
@ -40,6 +42,7 @@ class ContentBlockNode(DjangoObjectType, HiddenAndVisibleForMixin):
mine = graphene.Boolean()
bookmarks = graphene.List(ContentBlockBookmarkNode)
original_creator = graphene.Field('users.schema.PublicUserNode')
instrument_category = graphene.Field(InstrumentCategoryNode)
class Meta:
model = ContentBlock
@ -80,6 +83,17 @@ class ContentBlockNode(DjangoObjectType, HiddenAndVisibleForMixin):
content_block=self
)
@staticmethod
def resolve_instrument_category(root: ContentBlock, info, **kwargs):
if root.type == ContentBlock.INSTRUMENT:
for content in root.contents.raw_data:
if content['type'] == 'instrument' or content['type'] == 'basic_knowledge':
_id = content['value']['basic_knowledge']
instrument = BasicKnowledge.objects.get(id=_id)
category = instrument.new_type.category
return category
return None
def process_module_room_slug_block(content):
if content['type'] == 'module_room_slug':

View File

@ -203,7 +203,7 @@ module_1_chapter_2 = {
'description': 'Haben Sie sich beim Shoppen schon mal überlegt, aus welchem Beweggrund Sie ein bestimmtes Produkt eigentlich unbedingt haben wollten? Wir gehen im Folgenden anhand Ihres letzten Kleiderkaufs dieser Frage nach.',
'content_blocks': [
{
'type': 'base_society',
'type': 'instrument',
'title': 'Das Berufsbildungssystem',
'contents': [
{

View File

@ -299,6 +299,7 @@ type ContentBlockNode implements Node & ContentBlockInterface {
mine: Boolean
bookmarks: [ContentBlockBookmarkNode]
originalCreator: PublicUserNode
instrumentCategory: InstrumentCategoryNode
}
type ContentBlockNodeConnection {
@ -511,7 +512,7 @@ type InstrumentBookmarkNode implements Node {
instrument: InstrumentNode!
}
type InstrumentCategoryNode {
type InstrumentCategoryNode implements Node {
id: ID!
name: String!
background: String!
@ -539,7 +540,7 @@ type InstrumentNodeEdge {
cursor: String!
}
type InstrumentTypeNode {
type InstrumentTypeNode implements Node {
id: ID!
name: String!
category: InstrumentCategoryNode